Release 4.0.0-preview1-00051
[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     public class MessagePort : IDisposable
32     {
33         private static readonly object s_lock = new object();
34         private static readonly HashSet<string> s_portMap = new HashSet<string>();
35
36         // The name of the local message port
37         private readonly string _portName = null;
38
39         // If true the message port is a trusted port, otherwise false it is not
40         private readonly bool _trusted = false;
41
42         // The local message port ID
43         private int _portId = 0;
44
45         // If true the message port is listening, otherwise false it is not
46         private bool _listening = false;
47
48         private Interop.MessagePort.message_port_message_cb _messageCallBack;
49
50         /// <summary>
51         /// Initializes the instance of the MessagePort class.
52         /// </summary>
53         /// <param name="portName">The name of the local message port</param>
54         /// <param name="trusted">If true is the trusted message port of application, otherwise false</param>
55         /// <exception cref="System.InvalidOperationException">Thrown when portName is null or empty</exception>
56         /// <code>
57         /// MessagePort messagePort = new MessagePort("SenderPort", true);
58         /// </code>
59         public MessagePort(string portName, bool trusted)
60         {
61             if (String.IsNullOrEmpty(portName))
62             {
63                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "Invalid PortName", "PortName");
64             }
65             _portName = portName;
66             _trusted = trusted;
67         }
68
69         /// <summary>
70         /// Destructor of the MessagePort class.
71         /// </summary>
72         ~MessagePort()
73         {
74             Dispose(false);
75         }
76
77         /// <summary>
78         /// Called when a message is received.
79         /// </summary>
80         /// <code>
81         /// MessagePort messagePort = new MessagePort("SenderPort", true);
82         /// messagePort.MessageReceived += MessageReceivedCallback;
83         /// static void MessageReceivedCallback(object sender, MessageReceivedEventArgs e)
84         /// {
85         ///     Console.WriteLine("Message Received ");
86         ///     if (e.Remote.AppId != null) {
87         ///         Console.WriteLine("from :"+e.Remote.AppId);
88         ///     }
89         /// }
90         /// </code>
91         public event EventHandler<MessageReceivedEventArgs> MessageReceived;
92
93         /// <summary>
94         /// The name of the local message port
95         /// </summary>
96         public string PortName
97         {
98             get
99             {
100                 return _portName;
101             }
102         }
103         /// <summary>
104         /// If true the message port is a trusted port, otherwise false it is not
105         /// </summary>
106         public bool Trusted
107         {
108             get
109             {
110                 return _trusted;
111             }
112         }
113
114         /// <summary>
115         /// If true the message port is listening, otherwise false it is not
116         /// </summary>
117         public bool Listening
118         {
119             get
120             {
121                 return _listening;
122             }
123         }
124
125         /// <summary>
126         /// Register the local message port.
127         /// </summary>
128         /// <exception cref="System.InvalidOperationException">Thrown when portName is already used, when there is an invalid parameter, when out of memory, when there is an I/O error</exception>
129         /// <code>
130         /// MessagePort messagePort = new MessagePort("SenderPort", true);
131         /// messagePort.MessageReceived += MessageReceivedCallback;
132         /// messagePort.Listen();
133         /// </code>
134         public void Listen()
135         {
136             lock (s_lock)
137             {
138                 if (s_portMap.Contains(_portName))
139                 {
140                     throw new InvalidOperationException(_portName + " is already used");
141                 }
142                 _messageCallBack = (int localPortId, string remoteAppId, string remotePortName, bool trusted, IntPtr message, IntPtr userData) =>
143                 {
144                     MessageReceivedEventArgs args = new MessageReceivedEventArgs()
145                     {
146                         Message = new Bundle(new SafeBundleHandle(message, false))
147                     };
148
149                     if (!String.IsNullOrEmpty(remotePortName) && !String.IsNullOrEmpty(remoteAppId))
150                     {
151                         args.Remote = new RemoteValues()
152                         {
153                             AppId = remoteAppId,
154                             PortName = remotePortName,
155                             Trusted = trusted
156                         };
157                     }
158                     MessageReceived?.Invoke(this, args);
159                 };
160
161                 _portId = _trusted ?
162                             Interop.MessagePort.RegisterTrustedPort(_portName, _messageCallBack, IntPtr.Zero) :
163                             Interop.MessagePort.RegisterPort(_portName, _messageCallBack, IntPtr.Zero);
164
165                 if (_portId <= 0)
166                     throw new InvalidOperationException("Can't Listening with " + _portName);
167
168                 s_portMap.Add(_portName);
169                 _listening = true;
170             }
171         }
172
173         /// <summary>
174         /// Unregisters the local message port.
175         /// </summary>
176         /// <exception cref="System.InvalidOperationException">Thrown when messageport is already stopped, when there is an invalid parameter, when the port is not found, when out of memory, when there is an I/O error</exception>
177         /// <code>
178         /// MessagePort messagePort = new MessagePort("SenderPort", true);
179         /// messagePort.MessageReceived += MessageReceivedCallback;
180         /// messagePort.Listen();
181         /// using (var message = new Tizen.Application.Bundle())
182         /// {
183         ///     message.AddItem("message", "a_string");
184         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
185         /// }
186         /// messageProt.StopListening();
187         /// </code>
188         public void StopListening()
189         {
190             if (!_listening)
191             {
192                 throw new InvalidOperationException("Already stopped");
193             }
194
195             int ret = _trusted ?
196                         Interop.MessagePort.UnregisterTrustedPort(_portId) :
197                         Interop.MessagePort.UnregisterPort(_portId);
198
199             if (ret != (int)MessagePortError.None)
200             {
201                 MessagePortErrorFactory.ThrowException(ret);
202             }
203
204             lock (s_lock)
205             {
206                 s_portMap.Remove(_portName);
207             }
208             _portId = 0;
209             _listening = false;
210         }
211
212         /// <summary>
213         /// Sends a untrusted message to the message port of a remote application.
214         /// </summary>
215         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB</param>
216         /// <param name="remoteAppId">The ID of the remote application</param>
217         /// <param name="remotePortName">The name of the remote message port</param>
218         /// <exception cref="System.InvalidOperationException">Thrown when there is an invalid parameter, when the port is not found, when out of memory, when there is an I/O error</exception>
219         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB)</exception>
220         /// <code>
221         /// MessagePort messagePort = new MessagePort("SenderPort", true);
222         /// messagePort.MessageReceived += MessageReceivedCallback;
223         /// messagePort.Listen();
224         /// using (var message = new Tizen.Application.Bundle())
225         /// {
226         ///     message.AddItem("message", "a_string");
227         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
228         /// }
229         /// </code>
230         public void Send(Bundle message, string remoteAppId, string remotePortName)
231         {
232             Send(message, remoteAppId, remotePortName, false);
233         }
234
235         /// <summary>
236         /// Sends a message to the message port of a remote application.
237         /// </summary>
238         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB</param>
239         /// <param name="remoteAppId">The ID of the remote application</param>
240         /// <param name="remotePortName">The name of the remote message port</param>
241         /// <param name="trusted">If true the trusted message port of remote application otherwise false</param>
242         /// <exception cref="System.InvalidOperationException">Thrown when there is an invalid parameter, when the port is not found, when out of memory, when there is an I/O error</exception>
243         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB)</exception>
244         /// <code>
245         /// MessagePort messagePort = new MessagePort("SenderPort", true);
246         /// messagePort.MessageReceived += MessageReceivedCallback;
247         /// messagePort.Listen();
248         /// using (var message = new Tizen.Application.Bundle())
249         /// {
250         ///     message.AddItem("message", "a_string");
251         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort", true);
252         /// }
253         /// </code>
254         public void Send(Bundle message, string remoteAppId, string remotePortName, bool trusted)
255         {
256             if (!_listening)
257             {
258                 throw new InvalidOperationException("Should start listen before send");
259             }
260             if (message == null)
261             {
262                 throw new ArgumentNullException("message");
263             }
264             int ret = trusted ?
265                         Interop.MessagePort.SendTrustedMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId) :
266                         Interop.MessagePort.SendMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId);
267
268             if (ret != (int)MessagePortError.None)
269             {
270                 if (ret == (int)MessagePortError.MaxExceeded)
271                 {
272                     MessagePortErrorFactory.ThrowException(ret, "Message has exceeded the maximum limit(4KB)", "Message");
273                 }
274                 MessagePortErrorFactory.ThrowException(ret, "Can't send message");
275             }
276         }
277
278         /// <summary>
279         /// Releases the unmanaged resourced used by the MessagePort class specifying whether to perform a normal dispose operation.
280         /// </summary>
281         /// <param name="disposing">true for a normal dispose operation; false to finalize the handle.</param>
282         protected virtual void Dispose(bool disposing)
283         {
284             if (_listening)
285             {
286                 try
287                 {
288                     StopListening();
289                 }
290                 catch (Exception e)
291                 {
292                     Log.Warn(GetType().Namespace, "Exception in Dispose :" + e.Message);
293                 }
294             }
295         }
296
297         /// <summary>
298         /// Releases all resources used by the MessagePort class.
299         /// </summary>
300         public void Dispose()
301         {
302             Dispose(true);
303             GC.SuppressFinalize(this);
304         }
305     }
306 }