BuildRequires: pkgconfig(stt-engine)
BuildRequires: pkgconfig(tts-engine)
BuildRequires: pkgconfig(chromium-efl)
+BuildRequires: pkgconfig(libsessiond)
%if "%{profile}" == "tv"
BuildRequires: pkgconfig(trustzone-nwd)
%else
BuildRequires: pkgconfig(stt-engine)
BuildRequires: pkgconfig(tts-engine)
BuildRequires: pkgconfig(chromium-efl)
+BuildRequires: pkgconfig(libsessiond)
%if "%{profile}" == "tv"
BuildRequires: pkgconfig(trustzone-nwd)
%else
--- /dev/null
+/*
+* Copyright (c) 2023 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.
+*/
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ internal const string Session = "libsessiond.so.0";
+
+ internal const string Libc = "libc.so.6";
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 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.Text;
+using Tizen.System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Session
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void SubsessionReplyCallback(int result, IntPtr cb_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void SubsessionEventCallback(SubsessionEventInfoNative info, IntPtr cb_data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_add_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionAddUser(int session_uid, string user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_remove_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionRemoveUser(int session_uid, string user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_switch_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionSwitchUser(int session_uid, string next_user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_get_user_list", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionGetUserList(int session_uid, out IntPtr list, out int user_count);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_free_user_list", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void SubsessionFreeUserList(IntPtr list);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_get_current_user", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionGetCurrentUser(int session_uid, StringBuilder user);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_register_event_callback", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubesssionRegisterEventCallback(int session_uid, SessionEventType event_bits, SubsessionEventCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_unregister_event_callback", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubesssionUnregisterEventCallback(int session_uid, SessionEventType event_bits);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_event_wait_done", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionEventWaitDone(SubsessionEventInfoNative info);
+
+ [DllImport(Libraries.Libc, EntryPoint = "getuid", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int GetUID();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tizen.System
+{
+ /// <summary>
+ /// Provides methods to manage subsession users. Allows to register for events triggered by operations on subsession users.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public sealed class Session
+ {
+ /// <summary>
+ /// UID of current system user.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly int CurrentUID;
+
+ /// <summary>
+ /// Maximum length of any given user ID.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public const int MaxUserLength = 20;
+
+ /// <summary>
+ /// Special subsession ID, which is always present and does not represent any user.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public const string EmptyUser = "";
+
+ private static ConcurrentDictionary<int, Session> s_sessionInstances = new ConcurrentDictionary<int, Session>();
+
+ private readonly object _replyLock = new object();
+
+ private IDictionary<int, Interop.Session.SubsessionReplyCallback> _replyMap = new ConcurrentDictionary<int, Interop.Session.SubsessionReplyCallback>();
+
+ private int _replyID = 0;
+
+ private delegate void EventDelegate(SubsessionEventInfoNative infoNative, IntPtr data);
+
+ static Session()
+ {
+ CurrentUID = Interop.Session.GetUID();
+ }
+
+ private Session(int sessionUID)
+ {
+ SessionUID = sessionUID;
+ }
+
+ /// <summary>
+ /// Gets a Session object instance for a given sessionUID.
+ /// </summary>
+ /// <param name="sessionUID">Session UID of a requested Session object instance</param>
+ /// <returns>Returns requested Session object</returns>
+ /// <remarks>
+ /// To ensure thread safety, expilicit creation of Session object is not allowed.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static Session GetInstance(int sessionUID)
+ {
+ s_sessionInstances.TryAdd(sessionUID, new Session(sessionUID));
+ return s_sessionInstances[sessionUID];
+ }
+
+ /// <summary>
+ /// Gets session UID of this session object.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int SessionUID { get; private set; }
+
+ /// <summary>
+ /// Gets a list of all availible subsession user IDs for this session.
+ /// </summary>
+ /// <remarks>
+ /// The list of users depends on whether the session UID for this session object exists or not. If it
+ /// doesn't, the user list is empty (in particular this is not an error).
+ /// However if the session UID exists, the user list will contain the subsession
+ /// IDs (if they exist), but also the default value, which is "" (empty string, see EmptyUser field).
+ /// This doesn't mean that "" is a subsession ID in the same way as others; it is just a marker meaning that no subsession is
+ /// enabled.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public IReadOnlyList<string> GetUsers()
+ {
+
+ IntPtr ptr;
+ int count;
+
+ SessionError ret = Interop.Session.SubsessionGetUserList(SessionUID, out ptr, out count);
+ CheckError(ret, "Interop failed to get user list");
+
+ string[] users;
+ IntPtrToStringArray(ptr, count, out users);
+ Interop.Session.SubsessionFreeUserList(ptr);
+
+ return new List<string>(users);
+ }
+
+ /// <summary>
+ /// Gets a currently active subession user ID for this session.
+ /// </summary>
+ /// <remarks>
+ /// When no subsession is enabled, "" (empty string, see EmptyUser field) is returned.
+ /// This doesn't mean that "" is a subsession ID in the same way as others; it is just a marker meaning
+ /// that no subsession is enabled.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string GetCurrentUser()
+ {
+ StringBuilder user = new StringBuilder(MaxUserLength);
+ SessionError ret = Interop.Session.SubsessionGetCurrentUser(SessionUID, user);
+ CheckError(ret, "Interop failed to get current subsession user");
+
+ return user.ToString();
+ }
+
+ /// <summary>
+ /// Request new subsession to be created.
+ /// </summary>
+ /// <param name="userName">Subesssion user ID to be created</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException"> Provided subsession user ID already exists</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionAddUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ _replyMap.Remove((int)data);
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to complete adding a new subsession user");
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ return;
+ }
+
+ task.SetResult(true);
+ };
+
+ SessionError ret = Interop.Session.SubsessionAddUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for adding a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Request an existing subsession to be removed.
+ /// </summary>
+ /// <param name="userName">Existing subesssion user ID to be removed</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// Only inactive session ID can be removed. In order remove currently used session ID first switch to special
+ /// session ID "" (empty string, see EmptyUser), and only after switch completes, remove previously active session ID.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException">Provided subsession user ID does not exist</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionRemoveUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ _replyMap.Remove((int)data);
+
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to remove a subsession user");
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ return;
+ }
+
+ task.SetResult(true);
+ };
+
+ SessionError ret = Interop.Session.SubsessionRemoveUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for removing a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Request a subession to become currently active.
+ /// </summary>
+ /// <param name="userName">Existing subesssion user ID to be set as active</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// Special subsession ID "" (empty string, see EmptyUser) can be switched to, when it's required to deactivate
+ /// current subsession (this step is needed when current session is to be removed).
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException">Provided subsession user ID to switch to does not exist</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionSwitchUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ _replyMap.Remove((int)data);
+
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to switch to a different subsession user");
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ return;
+ }
+
+ task.SetResult(true);
+ };
+
+ SessionError ret = Interop.Session.SubsessionSwitchUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for switching a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Mark event as completed.
+ /// </summary>
+ /// <param name="subsessionEventArgs">Event argument of the event (obtained from said event)</param>
+ /// <remarks>
+ /// This method is assumed to be called from an event handler. You can only mark an event as completed
+ /// if you registered for in in the same process.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SubsessionEventMarkAsDone(SubsessionEventArgs subsessionEventArgs)
+ {
+ SessionError ret = Interop.Session.SubsessionEventWaitDone(subsessionEventArgs.SessionInfo);
+ CheckError(ret, $"Interop failed to mark this client's event (of type {subsessionEventArgs.SessionInfo.eventType}) as finished");
+ }
+
+ private void OnAddUserWait(SubsessionEventInfoNative infoNative, IntPtr data)
+ {
+ _addUserWaitHandler?.Invoke(this, new AddUserEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _addUserWaitCB = null;
+
+ private event EventHandler<AddUserEventArgs> _addUserWaitHandler = null;
+
+ private readonly object _addUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when a new subession user is successfully added to this session.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<AddUserEventArgs> AddUserWait
+ {
+ add
+ {
+ lock (_addUserWaitLock)
+ {
+ if (_addUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.AddUserWait, _addUserWaitCB, OnAddUserWait);
+ _addUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_addUserWaitLock)
+ {
+ _addUserWaitHandler -= value;
+ if (_addUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.AddUserWait, _addUserWaitCB);
+ }
+ }
+ }
+
+ private void OnRemoveUserWait(SubsessionEventInfoNative infoNative, IntPtr data)
+ {
+ _removeUserWaitHandler?.Invoke(this, new RemoveUserEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _removeUserWaitCB = null;
+
+ private event EventHandler<RemoveUserEventArgs> _removeUserWaitHandler = null;
+
+ private readonly object _removeUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when a subession user is successfully removed from this session.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<RemoveUserEventArgs> RemoveUserWait
+ {
+ add
+ {
+ lock (_removeUserWaitLock)
+ {
+ if (_removeUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.RemoveUserWait, _removeUserWaitCB, OnRemoveUserWait);
+ _removeUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_removeUserWaitLock)
+ {
+ _removeUserWaitHandler -= value;
+ if (_removeUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.RemoveUserWait, _removeUserWaitCB);
+ }
+ }
+ }
+
+ private void OnSwitchUserWait(SubsessionEventInfoNative infoNative, IntPtr data)
+ {
+ _switchUserWaitHandler?.Invoke(this, new SwitchUserWaitEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _switchUserWaitCB = null;
+
+ private event EventHandler<SwitchUserWaitEventArgs> _switchUserWaitHandler = null;
+
+ private readonly object _switchUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when an existing subession user has begun switching to an active state.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<SwitchUserWaitEventArgs> SwitchUserWait
+ {
+ add
+ {
+ lock (_switchUserWaitLock)
+ {
+ if (_switchUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.SwitchUserWait, _switchUserWaitCB, OnSwitchUserWait);
+ _switchUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_switchUserWaitLock)
+ {
+ _switchUserWaitHandler -= value;
+ if (_switchUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.SwitchUserWait, _switchUserWaitCB);
+ }
+ }
+ }
+
+ private void OnSwitchUserCompletion(SubsessionEventInfoNative infoNative, IntPtr data)
+ {
+ _switchUserCompletionHandler?.Invoke(this, new SwitchUserCompletionEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _switchUserCompletionCB = null;
+
+ private event EventHandler<SwitchUserCompletionEventArgs> _switchUserCompletionHandler = null;
+
+ private readonly object _switchUserCompletionLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when an existing subession user is successfully switched to an acvite state.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<SwitchUserCompletionEventArgs> SwitchUserCompletion
+ {
+ add
+ {
+ lock (_switchUserCompletionLock)
+ {
+ if (_switchUserCompletionHandler == null)
+ RegisterCallbackForEvent(SessionEventType.SwitchUserCompletion, _switchUserCompletionCB, OnSwitchUserCompletion);
+ _switchUserCompletionHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_switchUserCompletionLock)
+ {
+ _switchUserCompletionHandler -= value;
+ if (_switchUserCompletionHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.SwitchUserCompletion, _switchUserCompletionCB);
+ }
+ }
+ }
+
+ private void CheckError(SessionError ret, string msg)
+ {
+ if (ret == SessionError.None)
+ return;
+
+ Log.Error(SessionErrorFactory.LogTag, msg);
+ SessionErrorFactory.ThrowException(ret);
+ }
+
+ private void IntPtrToStringArray(IntPtr unmanagedArray, int size, out string[] managedArray)
+ {
+ managedArray = new string[size];
+
+ byte[] byteArray = new byte[MaxUserLength * size];
+ byte[] tmpArray = new byte[MaxUserLength];
+
+ Marshal.Copy(unmanagedArray, byteArray, 0, MaxUserLength * size);
+
+ for (int i = 0; i < size; i++)
+ {
+ Array.Copy(byteArray, MaxUserLength * i, tmpArray, 0, MaxUserLength);
+ managedArray[i] = Encoding.UTF8.GetString(tmpArray);
+ }
+ }
+
+ private void RegisterCallbackForEvent(SessionEventType eventType, Interop.Session.SubsessionEventCallback eventCallback,
+ EventDelegate delegateToSet)
+ {
+ eventCallback = new Interop.Session.SubsessionEventCallback(delegateToSet);
+ SessionError ret = Interop.Session.SubesssionRegisterEventCallback(SessionUID, eventType,
+ eventCallback, IntPtr.Zero);
+ CheckError(ret, $"Interop failed to register a callback for an event of type {eventType}");
+ }
+
+ private void UnregisterCallbackForEvent(SessionEventType eventType, Interop.Session.SubsessionEventCallback eventCallback)
+ {
+ SessionError ret = Interop.Session.SubesssionUnregisterEventCallback(SessionUID, eventType);
+ CheckError(ret, $"Interop failed to unregister a callback for an event of type {eventType}");
+ eventCallback = null;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.ComponentModel;
+
+namespace Tizen.System
+{
+ internal enum SessionEventType : int
+ {
+ AddUserWait = 1 << 0,
+ RemoveUserWait = 1 << 1,
+ SwitchUserWait = 1 << 2,
+ SwitchUserCompletion = 1 << 3,
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 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.IO;
+using Tizen.Internals.Errors;
+
+namespace Tizen.System
+{
+ internal enum SessionError
+ {
+ None = ErrorCode.None,
+ InvalidParameter = ErrorCode.InvalidParameter,
+ Io = ErrorCode.IoError,
+ OutOfMemory = ErrorCode.OutOfMemory,
+ AlreadyExists = ErrorCode.FileExists,
+ NotAvailible = ErrorCode.NoSuchDevice,
+ ResourceBusy = ErrorCode.ResourceBusy,
+ PermissionDenied = ErrorCode.PermissionDenied,
+ NotSupported = ErrorCode.NotSupported,
+ }
+
+ internal static class SessionErrorFactory
+ {
+ internal const string LogTag = "Tizen.System.Session";
+
+ internal static void ThrowException(SessionError err)
+ {
+ SessionError error = (SessionError)err;
+ if (error == SessionError.InvalidParameter)
+ {
+ throw new ArgumentException("Invalid parameter");
+ }
+ else if (error == SessionError.Io)
+ {
+ throw new IOException("I/O error");
+ }
+ else if (error == SessionError.OutOfMemory)
+ {
+ throw new OutOfMemoryException("Out of memory");
+ }
+ else if (error == SessionError.AlreadyExists)
+ {
+ throw new InvalidOperationException("Already exists");
+ }
+ else if (error == SessionError.NotAvailible)
+ {
+ throw new InvalidOperationException("Not availible");
+ }
+ else if (error == SessionError.ResourceBusy)
+ {
+ throw new InvalidOperationException("Resource busy");
+ }
+ else if (error == SessionError.PermissionDenied)
+ {
+ throw new UnauthorizedAccessException("Permission denied");
+ }
+ else if (error == SessionError.NotSupported)
+ {
+ throw new NotSupportedException("Not supported");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 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.ComponentModel;
+
+namespace Tizen.System
+{
+ /// <summary>
+ /// A generic subession event argument type.
+ /// </summary>
+ /// <remarks>
+ /// Use this as an argument type for generic subession event handlers.
+ /// You can check the event type that was invoked by checking a type of event arguments
+ /// during runtime - they all derive from this base class.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract class SubsessionEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Session UID of the session invoking the event
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int SessionUID { get; internal set; }
+
+ internal SubsessionEventInfoNative SessionInfo { get; set; }
+
+ internal SubsessionEventArgs(SubsessionEventInfoNative infoNative)
+ {
+ SessionUID = infoNative.sessionUID;
+ SessionInfo = infoNative;
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for AddUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AddUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// Added subsession user ID
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserName { get; internal set; }
+
+ internal AddUserEventArgs(SubsessionEventInfoNative infoNative)
+ : base(infoNative)
+ {
+ UserName = infoNative.addUser.userName;
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for RemoveUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class RemoveUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// Removed subsession user ID
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserName { get; internal set; }
+
+ internal RemoveUserEventArgs(SubsessionEventInfoNative infoNative)
+ : base(infoNative)
+ {
+ UserName = infoNative.removeUser.userName;
+ }
+ }
+
+ /// <summary>
+ /// A generic base class for Switch event types
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract class SwitchUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// ID of this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public long SwitchID { get; internal set; }
+
+ /// <summary>
+ /// Active subsession user ID before this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserNamePrev { get; internal set; }
+
+ /// <summary>
+ /// Active subsession ID after this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserNameNext { get; internal set; }
+
+ internal SwitchUserEventArgs(SubsessionEventInfoNative infoNative)
+ : base(infoNative)
+ {
+ SwitchID = infoNative.switchUser.switchID;
+ UserNamePrev = infoNative.switchUser.userNamePrev;
+ UserNameNext = infoNative.switchUser.userNameNext;
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for SwitchUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SwitchUserWaitEventArgs : SwitchUserEventArgs
+ {
+ internal SwitchUserWaitEventArgs(SubsessionEventInfoNative infoNative) : base(infoNative) { }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for SwitchUserCompletion event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SwitchUserCompletionEventArgs : SwitchUserEventArgs
+ {
+ internal SwitchUserCompletionEventArgs(SubsessionEventInfoNative infoNative) : base(infoNative) { }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2023 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.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Text;
+using Tizen.Internals;
+
+namespace Tizen.System
+{
+ [NativeStruct("subsession_event_info", Include = "sessiond.h", PkgConfig = "libsessiond")]
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct SubsessionEventInfoNative
+ {
+ [FieldOffset(0)]
+ public SessionEventType eventType;
+ [FieldOffset(4)]
+ public int sessionUID;
+ [FieldOffset(8)]
+ public AddUser addUser;
+ [FieldOffset(8)]
+ public RemoveUser removeUser;
+ [FieldOffset(8)]
+ public SwitchUser switchUser;
+ }
+
+ /*
+
+ Regarding AddUser, RemoveUser and SwitchUser structs:
+
+ Marshaling complex C/C++ structs containing C/C++ unions as their members is tricky.
+ Firstly, to handle C/C++ unions in C# one needs to provide an explicit memory layout.
+ It's vital to provide a correct alignment. Since our fields in the native C struct being
+ reflected here are already all divisible by 4, there's no need for fillers.
+ Secondly, when the C# marshaller encounters an array, it creates a managed counterpart
+ on the heap, but what we'll get on the stack is actually the reference to the array on the heap.
+ Any other structure containing basic types would be overlapping and thus invalidating that
+ reference (remember we're dealing with a union where all the elements (structs in our case)
+ start form the same address) and leading to a nasty run-time error:
+ "it contains an object field at offset X that is incorrectly aligned
+ or overlapped by a non-object field".
+ The only solution that works 100% of the times is unrolling the arrays down to
+ single bytes and turn off the C# marshaller.
+ Unfortunately the code is not pretty, but they say maturity is consent to an unfulfilled life.
+
+ */
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ internal struct AddUser
+ {
+ public byte userName_00;
+ public byte userName_01;
+ public byte userName_02;
+ public byte userName_03;
+ public byte userName_04;
+ public byte userName_05;
+ public byte userName_06;
+ public byte userName_07;
+ public byte userName_08;
+ public byte userName_09;
+ public byte userName_10;
+ public byte userName_11;
+ public byte userName_12;
+ public byte userName_13;
+ public byte userName_14;
+ public byte userName_15;
+ public byte userName_16;
+ public byte userName_17;
+ public byte userName_18;
+ public byte userName_19;
+
+ public string userName
+ {
+ get
+ {
+ return UnrolledBytesToStringConverter.Convert20(
+ userName_00, userName_01, userName_02, userName_03,
+ userName_04, userName_05, userName_06, userName_07,
+ userName_08, userName_09, userName_10, userName_11,
+ userName_12, userName_13, userName_14, userName_15,
+ userName_16, userName_17, userName_18, userName_19
+ );
+ }
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ internal struct RemoveUser
+ {
+ public byte userName_00;
+ public byte userName_01;
+ public byte userName_02;
+ public byte userName_03;
+ public byte userName_04;
+ public byte userName_05;
+ public byte userName_06;
+ public byte userName_07;
+ public byte userName_08;
+ public byte userName_09;
+ public byte userName_10;
+ public byte userName_11;
+ public byte userName_12;
+ public byte userName_13;
+ public byte userName_14;
+ public byte userName_15;
+ public byte userName_16;
+ public byte userName_17;
+ public byte userName_18;
+ public byte userName_19;
+ public string userName
+ {
+ get
+ {
+ return UnrolledBytesToStringConverter.Convert20(
+ userName_00, userName_01, userName_02, userName_03,
+ userName_04, userName_05, userName_06, userName_07,
+ userName_08, userName_09, userName_10, userName_11,
+ userName_12, userName_13, userName_14, userName_15,
+ userName_16, userName_17, userName_18, userName_19
+ );
+ }
+ }
+ }
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 4)]
+ internal struct SwitchUser
+ {
+ public Int64 switchID;
+ public byte userNamePrev_00;
+ public byte userNamePrev_01;
+ public byte userNamePrev_02;
+ public byte userNamePrev_03;
+ public byte userNamePrev_04;
+ public byte userNamePrev_05;
+ public byte userNamePrev_06;
+ public byte userNamePrev_07;
+ public byte userNamePrev_08;
+ public byte userNamePrev_09;
+ public byte userNamePrev_10;
+ public byte userNamePrev_11;
+ public byte userNamePrev_12;
+ public byte userNamePrev_13;
+ public byte userNamePrev_14;
+ public byte userNamePrev_15;
+ public byte userNamePrev_16;
+ public byte userNamePrev_17;
+ public byte userNamePrev_18;
+ public byte userNamePrev_19;
+
+ public string userNamePrev
+ {
+ get
+ {
+ return UnrolledBytesToStringConverter.Convert20(
+ userNamePrev_00, userNamePrev_01, userNamePrev_02, userNamePrev_03,
+ userNamePrev_04, userNamePrev_05, userNamePrev_06, userNamePrev_07,
+ userNamePrev_08, userNamePrev_09, userNamePrev_10, userNamePrev_11,
+ userNamePrev_12, userNamePrev_13, userNamePrev_14, userNamePrev_15,
+ userNamePrev_16, userNamePrev_17, userNamePrev_18, userNamePrev_19
+ );
+ }
+ }
+
+ public byte userNameNext_00;
+ public byte userNameNext_01;
+ public byte userNameNext_02;
+ public byte userNameNext_03;
+ public byte userNameNext_04;
+ public byte userNameNext_05;
+ public byte userNameNext_06;
+ public byte userNameNext_07;
+ public byte userNameNext_08;
+ public byte userNameNext_09;
+ public byte userNameNext_10;
+ public byte userNameNext_11;
+ public byte userNameNext_12;
+ public byte userNameNext_13;
+ public byte userNameNext_14;
+ public byte userNameNext_15;
+ public byte userNameNext_16;
+ public byte userNameNext_17;
+ public byte userNameNext_18;
+ public byte userNameNext_19;
+
+ public string userNameNext
+ {
+ get
+ {
+ return UnrolledBytesToStringConverter.Convert20(
+ userNameNext_00, userNameNext_01, userNameNext_02, userNameNext_03,
+ userNameNext_04, userNameNext_05, userNameNext_06, userNameNext_07,
+ userNameNext_08, userNameNext_09, userNameNext_10, userNameNext_11,
+ userNameNext_12, userNameNext_13, userNameNext_14, userNameNext_15,
+ userNameNext_16, userNameNext_17, userNameNext_18, userNameNext_19
+ );
+ }
+ }
+ }
+
+ internal static class UnrolledBytesToStringConverter
+ {
+ public static string Convert20(
+ byte val_00, byte val_01, byte val_02, byte val_03,
+ byte val_04, byte val_05, byte val_06, byte val_07,
+ byte val_08, byte val_09, byte val_10, byte val_11,
+ byte val_12, byte val_13, byte val_14, byte val_15,
+ byte val_16, byte val_17, byte val_18, byte val_19)
+ {
+ byte[] toConvert = new byte[]
+ {
+ val_00, val_01, val_02, val_03,
+ val_04, val_05, val_06, val_07,
+ val_08, val_09, val_10, val_11,
+ val_12, val_13, val_14, val_15,
+ val_16, val_17, val_18, val_19
+ };
+
+ return Encoding.UTF8.GetString(toConvert);
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Tizen\Tizen.csproj" />
+ <ProjectReference Include="..\Tizen.Log\Tizen.Log.csproj" />
+ </ItemGroup>
+
+</Project>