[Applications.UnitedService] Add a new module for services (#6743)
authorhjhun <36876573+hjhun@users.noreply.github.com>
Sat, 15 Mar 2025 00:10:57 +0000 (09:10 +0900)
committerGitHub <noreply@github.com>
Sat, 15 Mar 2025 00:10:57 +0000 (09:10 +0900)
* [Tizen.Applications] Add UnitedService

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Add debugging logs

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Initialize Tizen Core while calling OnCreate()

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Fix wrong implementation

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Handle commands

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Fix wrong implemenation about handling commands

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Change LogTag

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Applications] Fix Unload method

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Add ServiceManager

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Fix wrong descriptions

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Modify resource path

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Remove ServiceLoader class

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* [Tizen.Applications] Refactor UnitedService

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
---------

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
13 files changed:
src/Tizen.Applications.UnitedService/Interop/Interop.Libc.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Interop/Interop.Libraries.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Interop/Interop.PkgMgrInfo.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.csproj [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.sln [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/Log.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/Service.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceAssembly.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceEventType.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceInfo.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleChangedEventArgs.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleState.cs [new file with mode: 0644]
src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceManager.cs [new file with mode: 0644]

diff --git a/src/Tizen.Applications.UnitedService/Interop/Interop.Libc.cs b/src/Tizen.Applications.UnitedService/Interop/Interop.Libc.cs
new file mode 100644 (file)
index 0000000..ed22d04
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2025 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.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    internal static partial class Libc
+    {
+        [DllImport(Libraries.Libc, EntryPoint = "getenv")]
+        internal static extern IntPtr GetEnviornmentVariable(string name);
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Interop/Interop.Libraries.cs b/src/Tizen.Applications.UnitedService/Interop/Interop.Libraries.cs
new file mode 100644 (file)
index 0000000..ec4d2cc
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2025 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
+    {
+        public const string PackageManager = "libcapi-appfw-package-manager.so.0";
+        public const string Libc = "libc.so.6";
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Interop/Interop.PkgMgrInfo.cs b/src/Tizen.Applications.UnitedService/Interop/Interop.PkgMgrInfo.cs
new file mode 100644 (file)
index 0000000..bd46f66
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2025 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.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    internal static partial class PackageManagerInfo
+    {
+        internal enum ErrorCode
+        {
+            None = 0,
+            Error = -1,
+            InvalidParameter = -2,
+            NoSuchPackage = -3,
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate int PackageInfoPackageMetadataListCallback(string key, string value, IntPtr userData);
+        // int (*pkgmgrinfo_pkg_metadata_list_cb)(const char *key, const char *value, void *user_data);
+
+        [DllImport(Libraries.PackageManager, EntryPoint = "pkgmgrinfo_pkginfo_get_pkginfo")]
+        internal static extern ErrorCode PackageInfoGet(string packageId, out IntPtr handle);
+
+        [DllImport(Libraries.PackageManager, EntryPoint = "pkgmgrinfo_pkginfo_destroy_pkginfo")]
+        internal static extern ErrorCode PackageInfoDestroy(IntPtr handle);
+
+        [DllImport(Libraries.PackageManager, EntryPoint = "pkgmgrinfo_pkginfo_get_res_type")]
+        internal static extern ErrorCode PackageInfoGetResourceType(IntPtr handle, out IntPtr resourceType);
+
+        [DllImport(Libraries.PackageManager, EntryPoint = "pkgmgrinfo_pkginfo_get_res_version")]
+        internal static extern ErrorCode PackageInfoGetResourceVersion(IntPtr handle, out IntPtr resourceVersion);
+
+        [DllImport(Libraries.PackageManager, EntryPoint = "pkgmgrinfo_pkginfo_foreach_metadata")]
+        internal static extern ErrorCode PackageInfoForeachMetadata(IntPtr handle, PackageInfoPackageMetadataListCallback callback, IntPtr userData);
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.csproj b/src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.csproj
new file mode 100644 (file)
index 0000000..13660d0
--- /dev/null
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Tizen.Applications.Common\Tizen.Applications.Common.csproj" />
+    <ProjectReference Include="..\Tizen.Applications.Service\Tizen.Applications.Service.csproj" />
+    <ProjectReference Include="..\Tizen.Core\Tizen.Core.csproj" />
+    <ProjectReference Include="..\Tizen.Log\Tizen.Log.csproj" />
+    <ProjectReference Include="..\Tizen\Tizen.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.sln b/src/Tizen.Applications.UnitedService/Tizen.Applications.UnitedService.sln
new file mode 100644 (file)
index 0000000..df66774
--- /dev/null
@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.UnitedService", "Tizen.Applications.UnitedService.csproj", "{D7BF92FD-BB74-4BEF-B47D-F73A65157AF4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen", "..\Tizen\Tizen.csproj", "{2E04FB0F-03F4-40B0-BB25-5ADFB08C8DF3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Log", "..\Tizen.Log\Tizen.Log.csproj", "{8DA5B43B-63E9-460C-B64F-0B691539DC5D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.Common", "..\Tizen.Applications.Common\Tizen.Applications.Common.csproj", "{2AEDCAA7-543F-48A1-BEA3-CF3E14F6EDC2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.Service", "..\Tizen.Applications.Service\Tizen.Applications.Service.csproj", "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tizen.Core", "..\Tizen.Core\Tizen.Core.csproj", "{1BA08908-7B2B-42D1-A124-092CB177BF4C}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {D7BF92FD-BB74-4BEF-B47D-F73A65157AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {D7BF92FD-BB74-4BEF-B47D-F73A65157AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {D7BF92FD-BB74-4BEF-B47D-F73A65157AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {D7BF92FD-BB74-4BEF-B47D-F73A65157AF4}.Release|Any CPU.Build.0 = Release|Any CPU
+               {2E04FB0F-03F4-40B0-BB25-5ADFB08C8DF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {2E04FB0F-03F4-40B0-BB25-5ADFB08C8DF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {2E04FB0F-03F4-40B0-BB25-5ADFB08C8DF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {2E04FB0F-03F4-40B0-BB25-5ADFB08C8DF3}.Release|Any CPU.Build.0 = Release|Any CPU
+               {8DA5B43B-63E9-460C-B64F-0B691539DC5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {8DA5B43B-63E9-460C-B64F-0B691539DC5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {8DA5B43B-63E9-460C-B64F-0B691539DC5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {8DA5B43B-63E9-460C-B64F-0B691539DC5D}.Release|Any CPU.Build.0 = Release|Any CPU
+               {2AEDCAA7-543F-48A1-BEA3-CF3E14F6EDC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {2AEDCAA7-543F-48A1-BEA3-CF3E14F6EDC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {2AEDCAA7-543F-48A1-BEA3-CF3E14F6EDC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {2AEDCAA7-543F-48A1-BEA3-CF3E14F6EDC2}.Release|Any CPU.Build.0 = Release|Any CPU
+               {9A19103F-16F7-4668-BE54-9A1E7A4F7556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {9A19103F-16F7-4668-BE54-9A1E7A4F7556}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {9A19103F-16F7-4668-BE54-9A1E7A4F7556}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {9A19103F-16F7-4668-BE54-9A1E7A4F7556}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1BA08908-7B2B-42D1-A124-092CB177BF4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {1BA08908-7B2B-42D1-A124-092CB177BF4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1BA08908-7B2B-42D1-A124-092CB177BF4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {1BA08908-7B2B-42D1-A124-092CB177BF4C}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+               SolutionGuid = {CA8D5984-0B5A-4384-86ED-1ADEAD5829A1}
+       EndGlobalSection
+EndGlobal
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/Log.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/Log.cs
new file mode 100644 (file)
index 0000000..688a0d0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2025 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.Runtime.CompilerServices;
+
+namespace Tizen.Applications
+{
+    internal static class Log
+    {
+        private static string LogTag = "Tizen.Applications";
+
+        public static void Error(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+        {
+            Tizen.Log.Error(LogTag, message, file, func, line);
+        }
+
+        public static void Warn(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+        {
+            Tizen.Log.Warn(LogTag, message, file, func, line);
+        }
+
+        public static void Info(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+        {
+            Tizen.Log.Info(LogTag, message, file, func, line);
+        }
+
+        public static void Debug(string message, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+        {
+            Tizen.Log.Debug(LogTag, message, file, func, line);
+        }
+    }
+}
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/Service.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/Service.cs
new file mode 100644 (file)
index 0000000..e02c42b
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2025 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 System.ComponentModel;
+using Tizen.Core;
+
+namespace Tizen.Applications
+{
+    /// <summary>
+    /// Represents a Service controlled lifecycle.
+    /// </summary>
+    /// <remarks>
+    /// This class provides functionality related to managing the lifecycle of a Service.
+    /// It enables developers to handle events such as initialization, activation, deactivation, and destruction of the service.
+    /// By implementing this class, developers can define their own behavior for these events and customize the lifecycle of their services accordingly.
+    /// </remarks>
+    /// <since_tizen> 13 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public abstract class Service : IDisposable
+    {
+        private Tizen.Core.Task _task;
+        private bool _disposed;
+
+        /// <summary>
+        /// Initializes the service.
+        /// </summary>
+        /// <remarks>
+        /// This constructor initializes a new instance of the Service class.
+        /// </remarks>
+        /// <since_tizen> 13 </since_tizen>
+        public Service()
+        {
+            Log.Debug("Service");
+            TizenCore.Initialize();
+            State = ServiceLifecycleState.Initialized;
+            NotifyLifecycleChanged();
+        }
+
+        /// <summary>
+        /// Finalizer of the service class.
+        /// </summary>
+#pragma warning disable CA1063
+        ~Service()
+#pragma warning restore CA1063
+        {
+            Log.Debug("~Service");
+            Dispose(false);
+            TizenCore.Shutdown();
+        }
+
+        /// <summary>
+        /// The Id.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string Id => ServiceInfo?.Id;
+
+        /// <summary>
+        /// The name.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string Name => ServiceInfo?.Name;
+
+        internal event EventHandler<ServiceLifecycleChangedEventArgs> LifecycleChanged;
+
+        private void Service_LifecycleChanged(object sender, ServiceLifecycleChangedEventArgs e)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// The information of the service.
+        /// </summary>
+        /// <remarks>
+        /// This property is set before the OnCreate() is called, after the instance has been created.
+        /// It provides details about the current service such as its ID, name, version, and  other relevant information.
+        /// By accessing this property, developers can retrieve the necessary information about the service they are working on.
+        /// </remarks>
+        /// <since_tizen> 13 </since_tizen>
+        public ServiceInfo ServiceInfo { get; internal set; }
+
+        /// <summary>
+        /// The lifecycle state of the service.
+        /// </summary>
+        public ServiceLifecycleState State { get; internal set; }
+
+        /// <summary>
+        /// Runs the main loop of the service.
+        /// </summary>
+        /// <param name="args">The argument of the app control received event.</param>
+        /// <since_tizen> 13 </since_tizen>
+        public void Run(AppControlReceivedEventArgs args)
+        {
+            Log.Warn("Run");
+            if (_task != null && _task.Running)
+            {
+                Log.Info("Already running");
+                return;
+            }
+
+            _task = ServiceInfo.UseThread ? TizenCore.Spawn(Name) : TizenCore.Find("main");
+            _task.Post(() => { OnCreate(); });
+            SendAppControlReceivedEvent(args);
+        }
+
+        /// <summary>
+        /// Quits the main loop of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public void Quit()
+        {
+            Log.Warn("Quit");
+            if (_task == null || State == ServiceLifecycleState.Destroyed)
+            {
+                return;
+            }
+
+            _task.Post(() => { OnDestroy(); });
+            if (ServiceInfo.UseThread)
+            {
+                _task.Quit();
+                _task.Dispose();
+                _task = null;
+            }
+        }
+
+        internal void SendAppControlReceivedEvent(AppControlReceivedEventArgs args)
+        {
+            _task.Post(() =>
+            {
+                if (args == null)
+                {
+                    var appControl = new AppControl();
+                    var receivedAppControl = new ReceivedAppControl(appControl.SafeAppControlHandle);
+                    OnAppControlReceived(new AppControlReceivedEventArgs(receivedAppControl));
+                }
+                else
+                {
+                    OnAppControlReceived(args);
+                }
+            });
+        }
+
+        internal void SendSystemEvent(ServiceEventType eventType, EventArgs args)
+        {
+            _task.Post(() =>
+            {
+                switch (eventType)
+                {
+                    case ServiceEventType.LocaleChanged:
+                        OnLocaleChanged((LocaleChangedEventArgs)args);
+                        break;
+                    case ServiceEventType.LowMemory:
+                        OnLowMemory((LowMemoryEventArgs)args);
+                        break;
+                    case ServiceEventType.LowBattery:
+                        OnLowBattery((LowBatteryEventArgs)args);
+                        break;
+                    case ServiceEventType.RegionFormatChanged:
+                        OnRegionFormatChanged((RegionFormatChangedEventArgs)args);
+                        break;
+                    default:
+                        Log.Warn("Unknown Event Type: " + eventType);
+                        break;
+                }
+            });
+        }
+
+        private void NotifyLifecycleChanged()
+        {
+            var args = new ServiceLifecycleChangedEventArgs();
+            args.Service = this;
+            args.State = State;
+            CoreApplication.Post(() =>
+            {
+                LifecycleChanged?.Invoke(this, args);
+            });
+        }
+
+        /// <summary>
+        /// Override this method to define the behavior when the service is created.
+        /// Calling 'base.OnCreate()' is necessary in order to emit the 'ServiceLifecycleChanged' event with the 'ServiceLifecycleState.Created' state.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnCreate()
+        {
+            Log.Debug("OnCreate");
+            State = ServiceLifecycleState.Created;
+            NotifyLifecycleChanged();
+        }
+
+        /// <summary>
+        /// Overrides this method if want to handle behavior when the service receives the appcontrol message.
+        /// </summary>
+        /// <remarks>
+        /// This method provides a way to customize the response when the service receives an appcontrol message.
+        /// By overriding this method in your derived class, you can define specific actions based on the incoming arguments.
+        /// </remarks>
+        /// <param name="e">The appcontrol received event argument containing details about the received message.</param>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnAppControlReceived(AppControlReceivedEventArgs e)
+        {
+            Log.Debug("OnAppControlReceived");
+            if (State == ServiceLifecycleState.Created)
+            {
+                State = ServiceLifecycleState.Running;
+                NotifyLifecycleChanged();
+            }
+        }
+
+        /// <summary>
+        /// Override this method to handle the behavior when the service is destroyed.
+        /// If 'base.OnDestroy()' is not called, the 'ServiceLifecycleChanged' event with the 'ServiceLifecycleState.Destroyed' state will not be emitted.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnDestroy()
+        {
+            Log.Debug("OnDestroy");
+            State = ServiceLifecycleState.Destroyed;
+            NotifyLifecycleChanged();
+        }
+
+        /// <summary>
+        /// Overrides this method if want to handle behavior when the system language is changed.
+        /// </summary>
+        /// <param name="e">The locale changed event argument.</param>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnLocaleChanged(LocaleChangedEventArgs e) => Log.Debug("OnLocaleChanged");
+
+        /// <summary>
+        /// Overrides this method if want to handle behavior when the system battery is low.
+        /// </summary>
+        /// <param name="e">The low batter event argument.</param>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnLowBattery(LowBatteryEventArgs e) => Log.Debug("OnLowBattery");
+
+        /// <summary>
+        /// Overrides this method if want to handle behavior when the system memory is low.
+        /// </summary>
+        /// <param name="e">The low memory event argument.</param>
+        /// <since_tizen> 13 </since_tizen>c
+        protected virtual void OnLowMemory(LowMemoryEventArgs e) => Log.Debug("OnLowMemory");
+
+        /// <summary>
+        /// Overrides this method if want to handle behavior when the region format is changed.
+        /// </summary>
+        /// <param name="e">The region format changed event argument.</param>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void OnRegionFormatChanged(RegionFormatChangedEventArgs e) => Log.Debug("OnRegionFormatChanged");
+
+        /// <summary>
+        /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
+        /// </summary>
+        /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
+        /// <since_tizen> 13 </since_tizen>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!_disposed)
+            {
+                if (disposing)
+                {
+                    if (ServiceInfo.UseThread && _task != null)
+                    {
+                        _task.Dispose();
+                    }
+                    _task = null;
+                }
+
+                _disposed = true;
+            }
+        }
+
+        /// <summary>
+        /// Releases all resources used by the service class.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+    }
+}
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceAssembly.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceAssembly.cs
new file mode 100644 (file)
index 0000000..fb0c810
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+* Copyright (c) 2025 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.IO;
+using System.Reflection;
+using System.Runtime.Loader;
+
+using SystemIO = System.IO;
+
+namespace Tizen.Applications
+{
+    internal class ServiceAssemblyLoadContext : AssemblyLoadContext
+    {
+        public ServiceAssemblyLoadContext() : base(isCollectible: true) { }
+
+        protected override Assembly Load(AssemblyName name)
+        {
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// Represents a class that provides access to the methods and properties of the ServiceAssembly.
+    /// </summary>
+    /// <since_tizen> 13 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ServiceAssembly
+    {
+        private static readonly object _assemblyLock = new object();
+        private readonly string _assemblyPath;
+        private WeakReference<ServiceAssemblyLoadContext> _contextRef;
+        private Assembly _assembly = null;
+        private bool _loaded = false;
+
+        internal ServiceAssembly(string assemblyPath) { _assemblyPath = assemblyPath; }
+
+        internal void Load()
+        {
+            lock (_assemblyLock)
+            {
+                if (_loaded)
+                {
+                    return;
+                }
+
+                Log.Warn("Load(): " + _assemblyPath + " ++");
+                ServiceAssemblyLoadContext context = new ServiceAssemblyLoadContext();
+                _contextRef = new WeakReference<ServiceAssemblyLoadContext>(context);
+                string directoryPath = SystemIO.Path.GetDirectoryName(_assemblyPath);
+                string fileName = SystemIO.Path.GetFileNameWithoutExtension(_assemblyPath);
+                string nativeImagePath = directoryPath + "/.native_image/" + fileName + ".ni.dll";
+                Log.Debug("NativeImagePath=" + nativeImagePath + ", AssemblyPath=" + _assemblyPath);
+                _assembly = context.LoadFromNativeImagePath(nativeImagePath, _assemblyPath);
+                Log.Warn("Load(): " + _assemblyPath + " --");
+                _loaded = true;
+            }
+        }
+
+        internal bool IsLoaded => _loaded;
+
+        internal bool IsAlive => _contextRef.TryGetTarget(out var context) && context != null;
+
+        internal Service CreateInstance(string className)
+        {
+            lock (_assemblyLock)
+            {
+                return (Service)_assembly?.CreateInstance(className);
+            }
+        }
+
+        internal void Unload()
+        {
+            lock (_assemblyLock)
+            {
+                if (!_loaded)
+                {
+                    return;
+                }
+
+                Log.Warn("Unload(): " + _assemblyPath + " ++");
+                if (IsAlive)
+                {
+                    _contextRef.TryGetTarget(out var context);
+                    context?.Unload();
+                }
+
+                _assembly = null;
+                _loaded = false;
+                Log.Warn("Unload(): " + _assemblyPath + " --");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceEventType.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceEventType.cs
new file mode 100644 (file)
index 0000000..73011b1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2025 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.
+ */
+
+namespace Tizen.Applications
+{
+    internal enum ServiceEventType
+    {
+        LocaleChanged = 0,
+        LowBattery = 1,
+        LowMemory = 2,
+        RegionFormatChanged = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceInfo.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceInfo.cs
new file mode 100644 (file)
index 0000000..0524147
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2025 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;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
+
+using SystemIO = System.IO;
+
+namespace Tizen.Applications
+{
+    /// <summary>
+    /// This class provides properties to retrieve information the service.
+    /// </summary>
+    /// <since_tizen> 13 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ServiceInfo
+    {
+        private const string MetadataUnitedServiceExec = "http://tizen.org/metadata/united-service/exec";
+        private const string MetadataUnitedServiceType = "http://tizen.org/metadata/united-service/type";
+        private const string MetadataUnitedServiceId = "http://tizen.org/metadata/united-service/id";
+        private const string MetadataUnitedServiceUseThread = "http://tizen.org/metadata/united-service/use-thread";
+        private const string MetadataUnitedServiceClassName = "http://tizen.org/metadata/united-service/class-name";
+
+        internal ServiceInfo(string packageId)
+        {
+            PackageId = packageId;
+            Log.Warn("PackageId=" +  PackageId);
+        }
+
+        /// <summary>
+        /// The package ID of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string PackageId { get; private set; }
+
+        /// <summary>
+        /// The name of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string Name { get; private set; }
+
+        /// <summary>
+        /// The Id of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string Id {  get; private set; }
+
+        /// <summary>
+        /// The resource type of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string ResourceType { get; private set; }
+
+        /// <summary>
+        /// The resource version of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string ResourceVersion {  get; private set; }
+
+        /// <summary>
+        /// Gets the executable path of the application.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string ExecutablePath {  get; private set; }
+
+        /// <summary>
+        /// The resource path of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string ResourcePath { get; private set; }
+
+        /// <summary>
+        /// Check whether the service uses a thread or not.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public bool UseThread { get; private set; }
+
+        /// <summary>
+        /// The class name of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string ClassName { get; private set; }
+
+        /// <summary>
+        /// The type of the runtime of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public string Type { get; private set; }
+
+        /// <summary>
+        /// The metadata of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public IDictionary<string, string> Metadata { get; private set; }
+
+        private string ExecutableFile { get; set; }
+
+        /// <summary>
+        /// The assembly of the service.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public ServiceAssembly Assembly { get; internal set; }
+
+        private static void SetType(ServiceInfo info)
+        {
+            if (info.Metadata.TryGetValue(MetadataUnitedServiceType, out string type))
+            {
+                info.Type = type;
+                Log.Info("Type=" + type);
+            }
+            else
+            {
+                Log.Error("Failed to get type");
+            }
+        }
+
+        private static void SetName(ServiceInfo info)
+        {
+            if (!string.IsNullOrEmpty(info.ResourceType))
+            {
+                info.Name = info.ResourceType.Substring(info.ResourceType.LastIndexOf('.') + 1);
+            }
+            else
+            {
+                Log.Error("Failed to get name");
+            }
+        }
+
+        private static void SetId(ServiceInfo info)
+        {
+            if (info.Metadata.TryGetValue(MetadataUnitedServiceId, out string id))
+            {
+                info.Id = id;
+                Log.Info("Id=" + id);
+            }
+            else
+            {
+                Log.Error("Failed to get id");
+            }
+        }
+
+        private static void SetClassName(ServiceInfo info)
+        {
+            if (info.Metadata.TryGetValue(MetadataUnitedServiceClassName, out string className))
+            {
+                info.ClassName = className;
+                Log.Info("ClassName=" + className);
+            }
+            else
+            {
+                Log.Error("Failed to get class name");
+            }
+        }
+
+        private static void SetUseThread(ServiceInfo info)
+        {
+            info.UseThread = info.Metadata.TryGetValue(MetadataUnitedServiceUseThread, out string useThread) && useThread == "true";
+            Log.Info("UseThread=" + info.UseThread);
+        }
+
+        private static void SetResourcePath(ServiceInfo info)
+        {
+            string resourcePath = SystemIO.Path.GetDirectoryName(Application.Current.ApplicationInfo.ExecutablePath);
+            if (Directory.Exists(resourcePath + "/.res_mount/service/"))
+            {
+                info.ResourcePath = resourcePath + "/.res_mount/service/";
+            }
+            else if (Directory.Exists(resourcePath + "/.res_mount/"))
+            {
+                info.ResourcePath = resourcePath + "/.res_mount/";
+            }
+            else
+            {
+                info.ResourcePath = resourcePath + "/";
+            }
+        }
+
+        private static void SetExecutableFile(ServiceInfo info)
+        {
+            if (info.Metadata.TryGetValue(MetadataUnitedServiceExec, out string executableFile))
+            {
+                info.ExecutableFile = executableFile;
+                Log.Info("ExecutableFile=" + info.ExecutableFile);
+            }
+            else
+            {
+                Log.Error("Failed to find metadata. " + MetadataUnitedServiceExec);
+            }
+        }
+
+        private static void SetMetadata(ServiceInfo info, IntPtr handle)
+        {
+            Dictionary<string, string> metadata = new Dictionary<string, string>();
+            int callback(string key, string value, IntPtr userData)
+            {
+                Log.Info("key=" + key + ", value=" + value);
+                if (!string.IsNullOrEmpty(key))
+                {
+                    if (!metadata.ContainsKey(key))
+                    {
+                        metadata.Add(key, value);
+                    }
+                }
+                return 0;
+            }
+
+            var errorCode = Interop.PackageManagerInfo.PackageInfoForeachMetadata(handle, callback, IntPtr.Zero);
+            if (errorCode != Interop.PackageManagerInfo.ErrorCode.None)
+            {
+                Log.Error("Failed to retrieve meatadata. error = " + errorCode);
+            }
+
+            info.Metadata = metadata;
+        }
+
+        private static void SetResourceVersion(ServiceInfo info, IntPtr handle)
+        {
+            var errorCode = Interop.PackageManagerInfo.PackageInfoGetResourceVersion(handle, out IntPtr resourceVersionPtr);
+            if (errorCode != Interop.PackageManagerInfo.ErrorCode.None)
+            {
+                Log.Error("Failed to get resource version. error = " + errorCode);
+            }
+            else
+            {
+                info.ResourceVersion = Marshal.PtrToStringAnsi(resourceVersionPtr);
+            }
+        }
+
+        private static void SetResourceType(ServiceInfo info, IntPtr handle)
+        {
+            var errorCode = Interop.PackageManagerInfo.PackageInfoGetResourceType(handle, out IntPtr resourceTypePtr);
+            if (errorCode != Interop.PackageManagerInfo.ErrorCode.None)
+            {
+                Log.Error("Failed to get resource type. error = " + errorCode);
+            }
+            else
+            {
+                info.ResourceType = Marshal.PtrToStringAnsi(resourceTypePtr);
+            }
+        }
+
+        /// <summary>
+        /// Creates the information of the Service.
+        /// </summary>
+        /// <param name="packageId">The package ID of the service.</param>
+        /// <returns>The information of the service.</returns>
+        /// <since_tizen> 13 </since_tizen>
+        public static ServiceInfo Create(string packageId) 
+        {
+            Interop.PackageManagerInfo.ErrorCode errorCode = Interop.PackageManagerInfo.PackageInfoGet(packageId, out IntPtr handle);
+            if (errorCode != Interop.PackageManagerInfo.ErrorCode.None)
+            {
+                Log.Error("Failed to get package info. error = " + errorCode);
+                return null;
+            }
+
+            ServiceInfo info = new ServiceInfo(packageId);
+            if (info == null)
+            {
+                Log.Error("Failed to create ServiceInfo. package ID = " + packageId);
+                Interop.PackageManagerInfo.PackageInfoDestroy(handle);
+                return null;
+            }
+
+            SetResourceType(info, handle);
+            SetResourceVersion(info, handle);
+            SetMetadata(info, handle);
+            SetExecutableFile(info);
+            SetResourcePath(info);
+            SetUseThread(info);
+            SetClassName(info);
+            SetId(info);
+            SetName(info);
+            SetType(info);
+            info.ExecutablePath = info.ResourcePath + info.ExecutableFile;
+            info.Assembly = new ServiceAssembly(info.ExecutablePath);
+            Interop.PackageManagerInfo.PackageInfoDestroy(handle);
+            return info;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleChangedEventArgs.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleChangedEventArgs.cs
new file mode 100644 (file)
index 0000000..e078838
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.Applications
+{
+    /// <summary>
+    /// Event arguments for the service lifecycle change event.
+    /// </summary>
+    /// <since_tizen> 10 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ServiceLifecycleChangedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets the service object that triggered the event.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        public Service Service { get; internal set; }
+
+        /// <summary>
+        /// Gets the current state of the service lifecycle.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        public ServiceLifecycleState State { get; internal set; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleState.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceLifecycleState.cs
new file mode 100644 (file)
index 0000000..759dc63
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2025 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.Applications
+{
+    /// <summary>
+    /// Enumeration for the lifecycle state of the service.
+    /// </summary>
+    /// <since_tizen> 10 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public enum ServiceLifecycleState
+    {
+        /// <summary>
+        /// The initialized state.
+        /// This state is set when the service is initialized. The constructor of the service is called.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        Initialized = 0,
+
+        /// <summary>
+        /// The created state.
+        /// This state is set when the service is created. The 'OnCreate()' method of the service is called.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        Created = 1,
+
+        /// <summary>
+        /// The running state.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        Running = 2,
+
+        /// <summary>
+        /// The destroyed state.
+        /// This state is set when the service is destroyed. The 'OnDestroy()' method of the service is called.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        Destroyed = 3,
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceManager.cs b/src/Tizen.Applications.UnitedService/Tizen.Applications/ServiceManager.cs
new file mode 100644 (file)
index 0000000..87fe55d
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * 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.Generic;
+using System.Linq;
+using System.IO;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using Tizen.Core;
+using System.Collections.Concurrent;
+
+namespace Tizen.Applications
+{
+    /// <summary>
+    /// The ServiceManager provides methods and events related to managing services in the application.
+    /// </summary>
+    /// <since_tizen> 13 </since_tizen>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public static class ServiceManager
+    {
+        private static readonly ConcurrentDictionary<string, ServiceInfo> _serviceInfos = new ConcurrentDictionary<string, ServiceInfo>();
+        private static readonly ConcurrentDictionary<string, Service> _services = new ConcurrentDictionary<string, Service>();
+
+        static ServiceManager()
+        {
+        }
+
+        /// <summary>
+        /// Initializes the Service Manager.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public static void Initialize()
+        {
+            TizenCore.Initialize();
+            CreateServiceInfos();
+
+            var context = (CoreApplication)CoreApplication.Current;
+            context.AppControlReceived += OnAppControlReceived;
+            context.LowMemory += OnLowMemory;
+            context.LowBattery += OnLowBattery;
+            context.LocaleChanged += OnLocaleChanged;
+            context.RegionFormatChanged += OnRegionFormatChanged;
+        }
+
+        /// <summary>
+        /// Shuts down the Service Manager.
+        /// </summary>
+        /// <since_tize> 13 </since_tize>
+        public static void Shutdown()
+        {
+            QuitAll();
+            _serviceInfos.Clear();
+            _services.Clear();
+            TizenCore.Shutdown();
+        }
+
+        private static void CreateServiceInfos()
+        {
+            IntPtr servicePackages = Interop.Libc.GetEnviornmentVariable("SERVICE_PACKAGES");
+            if (servicePackages == IntPtr.Zero)
+            {
+                Log.Error("Failed to get environment variable");
+                return;
+            }
+
+            string packages = Marshal.PtrToStringAnsi(servicePackages);
+            if (string.IsNullOrEmpty(packages))
+            {
+                Log.Error("There is no resource packages");
+                return;
+            }
+
+            foreach (string packageId in packages.Split(':').ToList())
+            {
+                ServiceInfo info = ServiceInfo.Create(packageId);
+                if (info != null)
+                {
+                    _serviceInfos[info.ResourceType] = info;
+                }
+            }
+        }
+
+        private static void OnAppControlReceived(object sender, AppControlReceivedEventArgs args)
+        {
+            Log.Debug("OnAppControlReceived");
+            HandleAppControlReceivedEvent(args);
+        }
+
+        private static void OnLowMemory(object sender, LowMemoryEventArgs args)
+        {
+            SendSystemEvent(ServiceEventType.LowMemory, args);
+        }
+
+        private static void OnLowBattery(object sender, LowBatteryEventArgs args)
+        {
+            SendSystemEvent(ServiceEventType.LowBattery, args);
+        }
+
+        private static void OnLocaleChanged(object sender, LocaleChangedEventArgs args)
+        {
+            SendSystemEvent(ServiceEventType.LocaleChanged, args);
+        }
+
+        private static void OnRegionFormatChanged(object sender, RegionFormatChangedEventArgs args)
+        {
+            SendSystemEvent(ServiceEventType.RegionFormatChanged, args);
+        }
+
+        /// <summary>
+        /// Occurs when the lifecycle of the Service is changed.
+        /// </summary>
+        /// <remarks>
+        /// This event is raised when the state of the Service changes.
+        /// It provides information about the current state through the ServiceLifecycleChangedEventArgs argument.
+        /// </remarks>
+        /// <since_tizen> 13 </since_tizen>
+        public static event EventHandler<ServiceLifecycleChangedEventArgs> ServiceLifecycleChanged;
+
+        private static string GetCommand(ReceivedAppControl receivedAppControl)
+        {
+            string command = string.Empty;
+            try
+            {
+                command = receivedAppControl.ExtraData.Get<string>("__K_SERVICE_COMMAND");
+            }
+            catch (ArgumentNullException e)
+            {
+                Log.Warn("ArgumentNullException=" + e.Message); ;
+            }
+            catch (KeyNotFoundException e)
+            {
+                Log.Warn("KeyNotFoundException=" + e.Message); ;
+            }
+            catch (ArgumentException e)
+            {
+                Log.Warn("KeyNotFoundException=" + e.Message); ;
+            }
+
+            return command;
+        }
+
+        private static string GetResourceTypeFromReceivedAppControl(ReceivedAppControl receivedAppControl)
+        {
+            string resourceType = string.Empty;
+            try
+            {
+                var id = receivedAppControl.ExtraData.Get<string>("__K_SERVICE_ID");
+                if (!string.IsNullOrEmpty(id))
+                {
+                    resourceType = GetResourceTypeFromId(id);
+                }
+            }
+            catch (ArgumentNullException e)
+            {
+                Log.Warn("ArgumentNullException=" + e.Message); ;
+            }
+            catch (KeyNotFoundException e)
+            {
+                Log.Warn("KeyNotFoundException=" + e.Message); ;
+            }
+            catch (ArgumentException e)
+            {
+                Log.Warn("KeyNotFoundException=" + e.Message); ;
+            }
+
+            return resourceType;
+        }
+
+        private static string GetResourceTypeFromId(string id)
+        {
+            return _serviceInfos.Values.FirstOrDefault(info => info.Id == id)?.ResourceType ?? string.Empty;
+        }
+
+        private static void HandleAppControlReceivedEvent(AppControlReceivedEventArgs e)
+        {
+            if (e == null)
+            {
+                return;
+            }
+
+            string resourceType = GetResourceTypeFromId(e.ReceivedAppControl.ApplicationId);
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                resourceType = GetResourceTypeFromReceivedAppControl(e.ReceivedAppControl);
+            }
+
+            if (!string.IsNullOrEmpty(resourceType))
+            {
+                string command = GetCommand(e.ReceivedAppControl);
+
+                if (command == "QUIT")
+                {
+                    Service service = null;
+                    lock (_services)
+                    {
+                        if (_services.TryGetValue(resourceType, out service))
+                        {
+                            if (service.State != ServiceLifecycleState.Destroyed)
+                            {
+                                service.Quit();
+                            }
+                        }
+                    }
+                    return;
+                }
+
+                Run(resourceType, e);
+            }
+        }
+
+        /// <summary>
+        /// Retrieves the instance of currently running Services.
+        /// </summary>
+        /// <returns>An enumarable list containing all the active Services.</returns>
+        /// <since_tizen> 13 </since_tizen>
+        public static IEnumerable<Service> GetServices()
+        {
+            return _services.Values.ToList();
+        }
+
+        /// <summary>
+        /// Retrieves the information about available Services.
+        /// </summary>
+        /// <remarks>
+        /// This method provides details on services that are currently accessible rather than listing all installed services.
+        /// A Service's resource package may specify which applications have access through the "allowed-packages" setting.
+        /// During execution, the platform mounts the resource package in the application's resources directory.
+        /// </remarks>
+        /// <returns>An enumerable list of ServiceInfo objects.</returns>
+        /// <since_tizen> 13 </since_tizen>
+        public static IEnumerable<ServiceInfo> GetServiceInfos()
+        {
+            return _serviceInfos.Values.ToList();
+        }
+
+        private static void SendSystemEvent(ServiceEventType eventType, EventArgs args)
+        {
+            foreach (var service in _services.Values)
+            {
+                service.SendSystemEvent(eventType, args);
+            }
+        }
+
+        private static void OnServiceLifecycleChangedEvent(object sender, ServiceLifecycleChangedEventArgs args)
+        {
+            ServiceLifecycleChanged?.Invoke(sender, args);
+            if (args.State == ServiceLifecycleState.Destroyed)
+            {
+                args.Service.LifecycleChanged -= OnServiceLifecycleChangedEvent;
+                _services.TryRemove(args.Service.ServiceInfo.ResourceType, out _);
+            }
+        }
+
+        private static ServiceInfo Find(string resourceType)
+        {
+            if (!_serviceInfos.TryGetValue(resourceType, out var info))
+            {
+                throw new ArgumentException($"Failed to find ServiceInfo. resource type={resourceType}");
+            }
+
+            return info;
+        }
+
+        private static void Load(ServiceInfo info)
+        {
+            if (info == null)
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            try
+            {
+                if (!info.Assembly.IsLoaded)
+                {
+                    Log.Warn("ServiceAssembly.Load()=" + info.ExecutablePath + " ++");
+                    info.Assembly.Load();
+                    Log.Warn("ServiceAssembly.Load()=" + info.ExecutablePath + " --");
+                }
+            }
+            catch (FileLoadException e)
+            {
+                throw new InvalidOperationException(e.Message);
+            }
+            catch (BadImageFormatException e)
+            {
+                throw new InvalidOperationException(e.Message);
+            }
+        }
+
+        /// <summary>
+        /// Loads an assembly of the Service.
+        /// </summary>
+        /// <param name="resourceType">The resource type of the Service package.</param>
+        /// <remarks>
+        /// This method loads an assembly of the Service based on the specified resource type.
+        /// It throws an ArgumentException if the argument is invalid, or an InvalidOperationException if the operation fails due to any reason.
+        /// </remarks>
+        /// <exception cref="ArgumentException">Thrown when failed because of a invalid argument.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid operation.</exception>
+        /// <since_tizen> 13 </since_tizen>
+        public static void Load(string resourceType)
+        {
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            ServiceInfo info = Find(resourceType);
+            Load(info);
+        }
+
+        private static void Unload(ServiceInfo info)
+        {
+            if (info == null)
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            if (info.Assembly.IsLoaded)
+            {
+                info.Assembly.Unload();
+                for (int i = 0; info.Assembly.IsAlive && i < 10; i++)
+                {
+                    GC.Collect();
+                    GC.WaitForPendingFinalizers();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Unloads the specified Service assembly from memory.
+        /// </summary>
+        /// <remarks>
+        /// To use this method properly, the assembly of the gadget must be loaded using Load() with the custom context.
+        /// </remarks>
+        /// <param name="resourceType">The resource type of the Service package to unload.</param>
+        /// <exception cref="ArgumentException">Thrown when the argument passed is not valid.</exception>
+        /// <since_tizen> 13 </since_tizen>
+        public static void Unload(string resourceType)
+        {
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            ServiceInfo info = Find(resourceType);
+            Unload(info);
+        }
+
+        /// <summary>
+        /// Runs the main loop of the service.
+        /// </summary>
+        /// <remarks>
+        /// If the service is already running, this method sends the args to the running service.
+        /// </remarks>
+        /// <param name="resourceType">The resource type of the Service package.</param>
+        /// <param name="args">The arguments passed in the appcontrol message.</param>
+        /// <exception cref="ArgumentException">Thrown when failed because of a invalid argument.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid operation.</exception>
+        /// <since_tizen> 13 </since_tizen>
+        public static void Run(string resourceType, AppControlReceivedEventArgs args)
+        {
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            if (_services.TryGetValue(resourceType, out var service) && service.State != ServiceLifecycleState.Destroyed)
+            {
+                service.SendAppControlReceivedEvent(args);
+                return;
+            }
+
+            ServiceInfo info = Find(resourceType);
+            Load(info);
+
+            service = info.Assembly.CreateInstance(info.ClassName);
+            if (service == null)
+            {
+                throw new InvalidOperationException($"Failed to create instance. class name={info.ClassName}");
+            }
+
+            service.ServiceInfo = info;
+            service.LifecycleChanged += OnServiceLifecycleChangedEvent;
+            _services[resourceType] = service;
+
+            service.Run(args);
+        }
+
+        /// <summary>
+        /// Quits the main loop of the service.
+        /// </summary>
+        /// <param name="resourceType">The resource type of the Service package.</param>
+        /// <exception cref="ArgumentException">Thrown when failed because of a invalid argument.</exception>
+        /// <since_tizen> 13 </since_tizen>
+        public static void Quit(string resourceType)
+        {
+            if (string.IsNullOrEmpty(resourceType))
+            {
+                throw new ArgumentException("Invalid argument");
+            }
+
+            if (_services.TryGetValue(resourceType, out var service))
+            {
+                service.Quit();
+            }
+        }
+
+        /// <summary>
+        /// Runs the main loop of all services.
+        /// </summary>
+        /// <exception cref="ArgumentException">Thrown when failed because of a invalid argument.</exception>
+        /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid operation.</exception>
+        /// <since_tizen> 13 </since_tizen>
+        public static void RunAll()
+        {
+            foreach (var resourceType in _serviceInfos.Keys)
+            {
+                Run(resourceType, null);
+            }
+        }
+
+        /// <summary>
+        /// Quits the main loop of all running services.
+        /// </summary>
+        /// <since_tizen> 13 </since_tizen>
+        public static void QuitAll()
+        {
+            foreach (var service in _services.Values)
+            {
+                service.Quit();
+            }
+        }
+    }
+}
\ No newline at end of file