[Tizen.Applications.MessagePort] Remove checking name of remoteport (#135)
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.MessagePort / Tizen.Applications.Messages / MessagePort.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using Tizen.Applications;
20
21 namespace Tizen.Applications.Messages
22 {
23     /// <summary>
24     /// The message port API provides functions to send and receive messages between applications.
25     /// </summary>
26     /// <remarks>
27     /// The message port API provides functions for passing messages between applications. An application should register its own local port to receive messages from remote applications.
28     /// If a remote application sends a message, the registered callback function of the local port is called.
29     /// The trusted message-port API allows communications between applications that are signed by the same developer(author) certificate.
30     /// </remarks>
31     /// <since_tizen> 3 </since_tizen>
32     public class MessagePort : IDisposable
33     {
34         private static readonly object s_lock = new object();
35         private static readonly HashSet<string> s_portMap = new HashSet<string>();
36
37         // The name of the local message port
38         private readonly string _portName = null;
39
40         // If true the message port is a trusted port, otherwise false it is not
41         private readonly bool _trusted = false;
42
43         // The local message port ID
44         private int _portId = 0;
45
46         // If true the message port is listening, otherwise false it is not
47         private bool _listening = false;
48
49         private Interop.MessagePort.message_port_message_cb _messageCallBack;
50
51         /// <summary>
52         /// Initializes the instance of the MessagePort class.
53         /// </summary>
54         /// <param name="portName">The name of the local message port.</param>
55         /// <param name="trusted">If true, it is the trusted message port of application, otherwise false.</param>
56         /// <exception cref="System.ArgumentException">Thrown when portName is null or empty.</exception>
57         /// <example>
58         /// <code>
59         /// MessagePort messagePort = new MessagePort("SenderPort", true);
60         /// </code>
61         /// </example>
62         /// <since_tizen> 3 </since_tizen>
63         public MessagePort(string portName, bool trusted)
64         {
65             if (String.IsNullOrEmpty(portName))
66             {
67                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "Invalid PortName", "PortName");
68             }
69             _portName = portName;
70             _trusted = trusted;
71         }
72
73         /// <summary>
74         /// Destructor of the MessagePort class.
75         /// </summary>
76         ~MessagePort()
77         {
78             Dispose(false);
79         }
80
81         /// <summary>
82         /// Called when a message is received.
83         /// </summary>
84         /// <example>
85         /// <code>
86         /// MessagePort messagePort = new MessagePort("SenderPort", true);
87         /// messagePort.MessageReceived += MessageReceivedCallback;
88         /// static void MessageReceivedCallback(object sender, MessageReceivedEventArgs e)
89         /// {
90         ///     Console.WriteLine("Message Received ");
91         ///     if (e.Remote.AppId != null) {
92         ///         Console.WriteLine("from :"+e.Remote.AppId);
93         ///     }
94         /// }
95         /// </code>
96         /// </example>
97         /// <since_tizen> 3 </since_tizen>
98         public event EventHandler<MessageReceivedEventArgs> MessageReceived;
99
100         /// <summary>
101         /// The name of the local message port.
102         /// </summary>
103         /// <since_tizen> 3 </since_tizen>
104         public string PortName
105         {
106             get
107             {
108                 return _portName;
109             }
110         }
111         /// <summary>
112         /// If true, the message port is a trusted port, otherwise false.
113         /// </summary>
114         /// <since_tizen> 3 </since_tizen>
115         public bool Trusted
116         {
117             get
118             {
119                 return _trusted;
120             }
121         }
122
123         /// <summary>
124         /// If true, the message port is listening, otherwise false.
125         /// </summary>
126         /// <since_tizen> 3 </since_tizen>
127         public bool Listening
128         {
129             get
130             {
131                 return _listening;
132             }
133         }
134
135         /// <summary>
136         /// Register the local message port.
137         /// </summary>
138         /// <exception cref="System.InvalidOperationException">Thrown when portName is already used, when there is an I/O error.</exception>
139         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
140         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
141         /// <example>
142         /// <code>
143         /// MessagePort messagePort = new MessagePort("SenderPort", true);
144         /// messagePort.MessageReceived += MessageReceivedCallback;
145         /// messagePort.Listen();
146         /// </code>
147         /// </example>
148         /// <since_tizen> 3 </since_tizen>
149         public void Listen()
150         {
151             lock (s_lock)
152             {
153                 if (s_portMap.Contains(_portName))
154                 {
155                     MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, _portName + "is already used");
156                 }
157                 _messageCallBack = (int localPortId, string remoteAppId, string remotePortName, bool trusted, IntPtr message, IntPtr userData) =>
158                 {
159                     MessageReceivedEventArgs args = new MessageReceivedEventArgs()
160                     {
161                         Message = new Bundle(new SafeBundleHandle(message, false))
162                     };
163
164                     if (!String.IsNullOrEmpty(remoteAppId))
165                     {
166                         args.Remote = new RemoteValues()
167                         {
168                             AppId = remoteAppId,
169                             PortName = remotePortName,
170                             Trusted = trusted
171                         };
172                     }
173                     MessageReceived?.Invoke(this, args);
174                 };
175
176                 _portId = _trusted ?
177                             Interop.MessagePort.RegisterTrustedPort(_portName, _messageCallBack, IntPtr.Zero) :
178                             Interop.MessagePort.RegisterPort(_portName, _messageCallBack, IntPtr.Zero);
179
180                 if (_portId <= 0)
181                     MessagePortErrorFactory.ThrowException(_portId, "RegisterPort", _portName);
182
183                 s_portMap.Add(_portName);
184                 _listening = true;
185             }
186         }
187
188         /// <summary>
189         /// Unregisters the local message port.
190         /// </summary>
191         /// <exception cref="System.InvalidOperationException">Thrown when messageport is already stopped, when there is an I/O error, when the port is not found.</exception>
192         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
193         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
194         /// <example>
195         /// <code>
196         /// MessagePort messagePort = new MessagePort("SenderPort", true);
197         /// messagePort.MessageReceived += MessageReceivedCallback;
198         /// messagePort.Listen();
199         /// using (var message = new Tizen.Application.Bundle())
200         /// {
201         ///     message.AddItem("message", "a_string");
202         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
203         /// }
204         /// messagePort.StopListening();
205         /// </code>
206         /// </example>
207         /// <since_tizen> 3 </since_tizen>
208         public void StopListening()
209         {
210             if (!_listening)
211             {
212                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Already stopped");
213             }
214
215             int ret = _trusted ?
216                         Interop.MessagePort.UnregisterTrustedPort(_portId) :
217                         Interop.MessagePort.UnregisterPort(_portId);
218
219             if (ret != (int)MessagePortError.None)
220             {
221                 MessagePortErrorFactory.ThrowException(ret, "Error Unregister port");
222             }
223
224             lock (s_lock)
225             {
226                 s_portMap.Remove(_portName);
227             }
228             _portId = 0;
229             _listening = false;
230         }
231
232         /// <summary>
233         /// Sends an untrusted message to the message port of a remote application.
234         /// </summary>
235         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB.</param>
236         /// <param name="remoteAppId">The ID of the remote application.</param>
237         /// <param name="remotePortName">The name of the remote message port.</param>
238         /// <exception cref="System.InvalidOperationException">Thrown when there is an I/O error, when the port is not found.</exception>
239         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
240         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
241         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB).</exception>
242         /// <example>
243         /// <code>
244         /// MessagePort messagePort = new MessagePort("SenderPort", true);
245         /// messagePort.MessageReceived += MessageReceivedCallback;
246         /// messagePort.Listen();
247         /// using (var message = new Tizen.Application.Bundle())
248         /// {
249         ///     message.AddItem("message", "a_string");
250         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
251         /// }
252         /// </code>
253         /// </example>
254         /// <since_tizen> 3 </since_tizen>
255         public void Send(Bundle message, string remoteAppId, string remotePortName)
256         {
257             Send(message, remoteAppId, remotePortName, false);
258         }
259
260         /// <summary>
261         /// Sends a message to the message port of a remote application.
262         /// </summary>
263         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB.</param>
264         /// <param name="remoteAppId">The ID of the remote application.</param>
265         /// <param name="remotePortName">The name of the remote message port.</param>
266         /// <param name="trusted">If true, it is the trusted message port of remote application, otherwise false.</param>
267         /// <exception cref="System.InvalidOperationException">Thrown when there is an I/O error, when the port is not found.</exception>
268         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
269         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
270         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB).</exception>
271         /// <exception cref="System.UnauthorizedAccessException">Thrown when the remote application is not signed with the same certificate.</exception>
272         /// <example>
273         /// <code>
274         /// MessagePort messagePort = new MessagePort("SenderPort", true);
275         /// messagePort.MessageReceived += MessageReceivedCallback;
276         /// messagePort.Listen();
277         /// using (var message = new Tizen.Application.Bundle())
278         /// {
279         ///     message.AddItem("message", "a_string");
280         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort", true);
281         /// }
282         /// </code>
283         /// </example>
284         /// <since_tizen> 3 </since_tizen>
285         public void Send(Bundle message, string remoteAppId, string remotePortName, bool trusted)
286         {
287             if (!_listening)
288             {
289                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Should start listen before send");
290             }
291             if (message == null || message.SafeBundleHandle == null || message.SafeBundleHandle.IsInvalid)
292             {
293                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "message is null", "Message");
294             }
295             int ret = trusted ?
296                         Interop.MessagePort.SendTrustedMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId) :
297                         Interop.MessagePort.SendMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId);
298
299             if (ret != (int)MessagePortError.None)
300             {
301                 if (ret == (int)MessagePortError.MaxExceeded)
302                 {
303                     MessagePortErrorFactory.ThrowException(ret, "Message has exceeded the maximum limit(4KB)", "Message");
304                 }
305                 MessagePortErrorFactory.ThrowException(ret, "Can't send message");
306             }
307         }
308
309         /// <summary>
310         /// Releases the unmanaged resources used by the MessagePort class specifying whether to perform a normal dispose operation.
311         /// </summary>
312         /// <param name="disposing">true for a normal dispose operation; false to finalize the handle.</param>
313         /// <since_tizen> 3 </since_tizen>
314         protected virtual void Dispose(bool disposing)
315         {
316             if (_listening)
317             {
318                 try
319                 {
320                     StopListening();
321                 }
322                 catch (Exception e)
323                 {
324                     Log.Warn(GetType().Namespace, "Exception in Dispose :" + e.Message);
325                 }
326             }
327         }
328
329         /// <summary>
330         /// Releases all resources used by the MessagePort class.
331         /// </summary>
332         /// <since_tizen> 3 </since_tizen>
333         public void Dispose()
334         {
335             Dispose(true);
336             GC.SuppressFinalize(this);
337         }
338     }
339 }