--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+<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>
--- /dev/null
+
+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
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /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.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
--- /dev/null
+/*
+ * 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
--- /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.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