b1bd8184b9d561b873e602483885028bb7de1dcf
[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, it is the trusted message port of application, otherwise false.</param>
55         /// <exception cref="System.ArgumentException">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.
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.
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 I/O error.</exception>
129         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
130         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
131         /// <code>
132         /// MessagePort messagePort = new MessagePort("SenderPort", true);
133         /// messagePort.MessageReceived += MessageReceivedCallback;
134         /// messagePort.Listen();
135         /// </code>
136         public void Listen()
137         {
138             lock (s_lock)
139             {
140                 if (s_portMap.Contains(_portName))
141                 {
142                     MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, _portName + "is already used");
143                 }
144                 _messageCallBack = (int localPortId, string remoteAppId, string remotePortName, bool trusted, IntPtr message, IntPtr userData) =>
145                 {
146                     MessageReceivedEventArgs args = new MessageReceivedEventArgs()
147                     {
148                         Message = new Bundle(new SafeBundleHandle(message, false))
149                     };
150
151                     if (!String.IsNullOrEmpty(remotePortName) && !String.IsNullOrEmpty(remoteAppId))
152                     {
153                         args.Remote = new RemoteValues()
154                         {
155                             AppId = remoteAppId,
156                             PortName = remotePortName,
157                             Trusted = trusted
158                         };
159                     }
160                     MessageReceived?.Invoke(this, args);
161                 };
162
163                 _portId = _trusted ?
164                             Interop.MessagePort.RegisterTrustedPort(_portName, _messageCallBack, IntPtr.Zero) :
165                             Interop.MessagePort.RegisterPort(_portName, _messageCallBack, IntPtr.Zero);
166
167                 if (_portId <= 0)
168                     MessagePortErrorFactory.ThrowException(_portId, "RegisterPort", _portName);
169
170                 s_portMap.Add(_portName);
171                 _listening = true;
172             }
173         }
174
175         /// <summary>
176         /// Unregisters the local message port.
177         /// </summary>
178         /// <exception cref="System.InvalidOperationException">Thrown when messageport is already stopped, when there is an I/O error, when the port is not found.</exception>
179         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
180         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
181         /// <code>
182         /// MessagePort messagePort = new MessagePort("SenderPort", true);
183         /// messagePort.MessageReceived += MessageReceivedCallback;
184         /// messagePort.Listen();
185         /// using (var message = new Tizen.Application.Bundle())
186         /// {
187         ///     message.AddItem("message", "a_string");
188         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
189         /// }
190         /// messagePort.StopListening();
191         /// </code>
192         public void StopListening()
193         {
194             if (!_listening)
195             {
196                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Already stopped");
197             }
198
199             int ret = _trusted ?
200                         Interop.MessagePort.UnregisterTrustedPort(_portId) :
201                         Interop.MessagePort.UnregisterPort(_portId);
202
203             if (ret != (int)MessagePortError.None)
204             {
205                 MessagePortErrorFactory.ThrowException(ret, "Error Unregister port");
206             }
207
208             lock (s_lock)
209             {
210                 s_portMap.Remove(_portName);
211             }
212             _portId = 0;
213             _listening = false;
214         }
215
216         /// <summary>
217         /// Sends an untrusted message to the message port of a remote application.
218         /// </summary>
219         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB.</param>
220         /// <param name="remoteAppId">The ID of the remote application.</param>
221         /// <param name="remotePortName">The name of the remote message port.</param>
222         /// <exception cref="System.InvalidOperationException">Thrown when there is an I/O error, when the port is not found.</exception>
223         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
224         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
225         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB).</exception>
226         /// <code>
227         /// MessagePort messagePort = new MessagePort("SenderPort", true);
228         /// messagePort.MessageReceived += MessageReceivedCallback;
229         /// messagePort.Listen();
230         /// using (var message = new Tizen.Application.Bundle())
231         /// {
232         ///     message.AddItem("message", "a_string");
233         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
234         /// }
235         /// </code>
236         public void Send(Bundle message, string remoteAppId, string remotePortName)
237         {
238             Send(message, remoteAppId, remotePortName, false);
239         }
240
241         /// <summary>
242         /// Sends a message to the message port of a remote application.
243         /// </summary>
244         /// <param name="message">The message to be passed to the remote application, the recommended message size is under 4KB.</param>
245         /// <param name="remoteAppId">The ID of the remote application.</param>
246         /// <param name="remotePortName">The name of the remote message port.</param>
247         /// <param name="trusted">If true, it is the trusted message port of remote application, otherwise false.</param>
248         /// <exception cref="System.InvalidOperationException">Thrown when there is an I/O error, when the port is not found.</exception>
249         /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
250         /// <exception cref="System.OutOfMemoryException">Thrown when out of memory.</exception>
251         /// <exception cref="System.ArgumentOutOfRangeException">Thrown when message has exceeded the maximum limit(4KB).</exception>
252         /// <exception cref="System.UnauthorizedAccessException">Thrown when the remote application is not signed with the same certificate.</exception>
253         /// <code>
254         /// MessagePort messagePort = new MessagePort("SenderPort", true);
255         /// messagePort.MessageReceived += MessageReceivedCallback;
256         /// messagePort.Listen();
257         /// using (var message = new Tizen.Application.Bundle())
258         /// {
259         ///     message.AddItem("message", "a_string");
260         ///     messagePort.Send(message, "ReceiverAppID", "ReceiverPort", true);
261         /// }
262         /// </code>
263         public void Send(Bundle message, string remoteAppId, string remotePortName, bool trusted)
264         {
265             if (!_listening)
266             {
267                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Should start listen before send");
268             }
269             if (message == null || message.SafeBundleHandle == null || message.SafeBundleHandle.IsInvalid)
270             {
271                 MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "message is null", "Message");
272             }
273             int ret = trusted ?
274                         Interop.MessagePort.SendTrustedMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId) :
275                         Interop.MessagePort.SendMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId);
276
277             if (ret != (int)MessagePortError.None)
278             {
279                 if (ret == (int)MessagePortError.MaxExceeded)
280                 {
281                     MessagePortErrorFactory.ThrowException(ret, "Message has exceeded the maximum limit(4KB)", "Message");
282                 }
283                 MessagePortErrorFactory.ThrowException(ret, "Can't send message");
284             }
285         }
286
287         /// <summary>
288         /// Releases the unmanaged resources used by the MessagePort class specifying whether to perform a normal dispose operation.
289         /// </summary>
290         /// <param name="disposing">true for a normal dispose operation; false to finalize the handle.</param>
291         protected virtual void Dispose(bool disposing)
292         {
293             if (_listening)
294             {
295                 try
296                 {
297                     StopListening();
298                 }
299                 catch (Exception e)
300                 {
301                     Log.Warn(GetType().Namespace, "Exception in Dispose :" + e.Message);
302                 }
303             }
304         }
305
306         /// <summary>
307         /// Releases all resources used by the MessagePort class.
308         /// </summary>
309         public void Dispose()
310         {
311             Dispose(true);
312             GC.SuppressFinalize(this);
313         }
314     }
315 }