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