/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using Tizen.Applications;
namespace Tizen.Applications.Messages
{
///
/// The message port API provides functions to send and receive messages between applications.
///
///
/// 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.
/// If a remote application sends a message, the registered callback function of the local port is called.
/// The trusted message-port API allows communications between applications that are signed by the same developer(author) certificate.
///
/// 3
public class MessagePort : IDisposable
{
private static readonly object s_lock = new object();
private static readonly HashSet s_portMap = new HashSet();
// The name of the local message port
private readonly string _portName = null;
// If true the message port is a trusted port, otherwise false it is not
private readonly bool _trusted = false;
// The local message port ID
private int _portId = 0;
// If true the message port is listening, otherwise false it is not
private bool _listening = false;
private Interop.MessagePort.message_port_message_cb _messageCallBack;
///
/// Initializes the instance of the MessagePort class.
///
/// The name of the local message port.
/// If true, it is the trusted message port of application, otherwise false.
/// Thrown when portName is null or empty.
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
///
///
/// 3
public MessagePort(string portName, bool trusted)
{
if (String.IsNullOrEmpty(portName))
{
MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "Invalid PortName", "PortName");
}
_portName = portName;
_trusted = trusted;
}
///
/// Destructor of the MessagePort class.
///
~MessagePort()
{
Dispose(false);
}
///
/// Called when a message is received.
///
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
/// messagePort.MessageReceived += MessageReceivedCallback;
/// static void MessageReceivedCallback(object sender, MessageReceivedEventArgs e)
/// {
/// Console.WriteLine("Message Received ");
/// if (e.Remote.AppId != null) {
/// Console.WriteLine("from :"+e.Remote.AppId);
/// }
/// }
///
///
/// 3
public event EventHandler MessageReceived;
///
/// The name of the local message port.
///
/// 3
public string PortName
{
get
{
return _portName;
}
}
///
/// If true, the message port is a trusted port, otherwise false.
///
/// 3
public bool Trusted
{
get
{
return _trusted;
}
}
///
/// If true, the message port is listening, otherwise false.
///
/// 3
public bool Listening
{
get
{
return _listening;
}
}
///
/// Register the local message port.
///
/// Thrown when portName is already used, when there is an I/O error.
/// Thrown when there is an invalid parameter.
/// Thrown when out of memory.
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
/// messagePort.MessageReceived += MessageReceivedCallback;
/// messagePort.Listen();
///
///
/// 3
public void Listen()
{
lock (s_lock)
{
if (s_portMap.Contains(_portName))
{
MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, _portName + "is already used");
}
_messageCallBack = (int localPortId, string remoteAppId, string remotePortName, bool trusted, IntPtr message, IntPtr userData) =>
{
MessageReceivedEventArgs args = new MessageReceivedEventArgs()
{
Message = new Bundle(new SafeBundleHandle(message, false))
};
if (!String.IsNullOrEmpty(remotePortName) && !String.IsNullOrEmpty(remoteAppId))
{
args.Remote = new RemoteValues()
{
AppId = remoteAppId,
PortName = remotePortName,
Trusted = trusted
};
}
MessageReceived?.Invoke(this, args);
};
_portId = _trusted ?
Interop.MessagePort.RegisterTrustedPort(_portName, _messageCallBack, IntPtr.Zero) :
Interop.MessagePort.RegisterPort(_portName, _messageCallBack, IntPtr.Zero);
if (_portId <= 0)
MessagePortErrorFactory.ThrowException(_portId, "RegisterPort", _portName);
s_portMap.Add(_portName);
_listening = true;
}
}
///
/// Unregisters the local message port.
///
/// Thrown when messageport is already stopped, when there is an I/O error, when the port is not found.
/// Thrown when there is an invalid parameter.
/// Thrown when out of memory.
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
/// messagePort.MessageReceived += MessageReceivedCallback;
/// messagePort.Listen();
/// using (var message = new Tizen.Application.Bundle())
/// {
/// message.AddItem("message", "a_string");
/// messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
/// }
/// messagePort.StopListening();
///
///
/// 3
public void StopListening()
{
if (!_listening)
{
MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Already stopped");
}
int ret = _trusted ?
Interop.MessagePort.UnregisterTrustedPort(_portId) :
Interop.MessagePort.UnregisterPort(_portId);
if (ret != (int)MessagePortError.None)
{
MessagePortErrorFactory.ThrowException(ret, "Error Unregister port");
}
lock (s_lock)
{
s_portMap.Remove(_portName);
}
_portId = 0;
_listening = false;
}
///
/// Sends an untrusted message to the message port of a remote application.
///
/// The message to be passed to the remote application, the recommended message size is under 4KB.
/// The ID of the remote application.
/// The name of the remote message port.
/// Thrown when there is an I/O error, when the port is not found.
/// Thrown when there is an invalid parameter.
/// Thrown when out of memory.
/// Thrown when message has exceeded the maximum limit(4KB).
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
/// messagePort.MessageReceived += MessageReceivedCallback;
/// messagePort.Listen();
/// using (var message = new Tizen.Application.Bundle())
/// {
/// message.AddItem("message", "a_string");
/// messagePort.Send(message, "ReceiverAppID", "ReceiverPort");
/// }
///
///
/// 3
public void Send(Bundle message, string remoteAppId, string remotePortName)
{
Send(message, remoteAppId, remotePortName, false);
}
///
/// Sends a message to the message port of a remote application.
///
/// The message to be passed to the remote application, the recommended message size is under 4KB.
/// The ID of the remote application.
/// The name of the remote message port.
/// If true, it is the trusted message port of remote application, otherwise false.
/// Thrown when there is an I/O error, when the port is not found.
/// Thrown when there is an invalid parameter.
/// Thrown when out of memory.
/// Thrown when message has exceeded the maximum limit(4KB).
/// Thrown when the remote application is not signed with the same certificate.
///
///
/// MessagePort messagePort = new MessagePort("SenderPort", true);
/// messagePort.MessageReceived += MessageReceivedCallback;
/// messagePort.Listen();
/// using (var message = new Tizen.Application.Bundle())
/// {
/// message.AddItem("message", "a_string");
/// messagePort.Send(message, "ReceiverAppID", "ReceiverPort", true);
/// }
///
///
/// 3
public void Send(Bundle message, string remoteAppId, string remotePortName, bool trusted)
{
if (!_listening)
{
MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidOperation, "Should start listen before send");
}
if (message == null || message.SafeBundleHandle == null || message.SafeBundleHandle.IsInvalid)
{
MessagePortErrorFactory.ThrowException((int)MessagePortError.InvalidParameter, "message is null", "Message");
}
int ret = trusted ?
Interop.MessagePort.SendTrustedMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId) :
Interop.MessagePort.SendMessageWithLocalPort(remoteAppId, remotePortName, message.SafeBundleHandle, _portId);
if (ret != (int)MessagePortError.None)
{
if (ret == (int)MessagePortError.MaxExceeded)
{
MessagePortErrorFactory.ThrowException(ret, "Message has exceeded the maximum limit(4KB)", "Message");
}
MessagePortErrorFactory.ThrowException(ret, "Can't send message");
}
}
///
/// Releases the unmanaged resources used by the MessagePort class specifying whether to perform a normal dispose operation.
///
/// true for a normal dispose operation; false to finalize the handle.
/// 3
protected virtual void Dispose(bool disposing)
{
if (_listening)
{
try
{
StopListening();
}
catch (Exception e)
{
Log.Warn(GetType().Namespace, "Exception in Dispose :" + e.Message);
}
}
}
///
/// Releases all resources used by the MessagePort class.
///
/// 3
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}