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