[Package-Manager] Fixed code to avoid deadlock in async methods
[platform/core/csapi/tizenfx.git] / Tizen.Applications / Tizen.Applications / PackageManager.cs
1 // Copyright 2016 by Samsung Electronics, Inc.,
2 //
3 // This software is the confidential and proprietary information
4 // of Samsung Electronics, Inc. ("Confidential Information"). You
5 // shall not disclose such Confidential Information and shall use
6 // it only in accordance with the terms of the license agreement
7 // you entered into with Samsung.
8
9 using System;
10 using System.Collections.Generic;
11 using System.Threading.Tasks;
12
13 namespace Tizen.Applications
14 {
15     /// <summary>
16     /// PackageManager class. This class has the methods and events of the PackageManager.
17     /// </summary>
18     /// <remarks>
19     /// The package manager is one of the core modules of Tizen application framework, and responsible for getting their information.
20     /// You can also retrieve information related to the packages that are installed on the device.
21     /// </remarks>
22     public static class PackageManager
23     {
24         private const string LogTag = "Tizen.Applications";
25         private static SafePackageManagerHandle s_handle = new SafePackageManagerHandle();
26
27         private static Interop.PackageManager.EventStatus s_eventStatus;
28         private static event EventHandler<PackageManagerEventArgs> s_installEventHandler;
29         private static event EventHandler<PackageManagerEventArgs> s_uninstallEventHandler;
30         private static event EventHandler<PackageManagerEventArgs> s_updateEventHandler;
31
32         private static Interop.PackageManager.PackageManagerEventCallback s_packageManagerEventCallback;
33
34         /// <summary>
35         /// InstallProgressChanged event. This event is occurred when a package is getting installed and the progress of the request to the package manager changes.
36         /// </summary>
37         public static event EventHandler<PackageManagerEventArgs> InstallProgressChanged
38         {
39             add
40             {
41                 RegisterPackageManagerEventIfNeeded(GetFlaggedEventStatus(Interop.PackageManager.EventStatus.Install));
42                 s_installEventHandler += value;
43             }
44             remove
45             {
46                 s_installEventHandler -= value;
47                 UnregisterPackageManagerEventIfNeeded(GetUnflaggedEventStatus(Interop.PackageManager.EventStatus.Install));
48             }
49         }
50
51         /// <summary>
52         /// UninstallProgressChanged event. This event is occurred when a package is getting uninstalled and the progress of the request to the package manager changes.
53         /// </summary>
54         public static event EventHandler<PackageManagerEventArgs> UninstallProgressChanged
55         {
56             add
57             {
58                 RegisterPackageManagerEventIfNeeded(GetFlaggedEventStatus(Interop.PackageManager.EventStatus.Unstall));
59                 s_uninstallEventHandler += value;
60             }
61             remove
62             {
63                 s_uninstallEventHandler -= value;
64                 UnregisterPackageManagerEventIfNeeded(GetUnflaggedEventStatus(Interop.PackageManager.EventStatus.Unstall));
65             }
66         }
67
68         /// <summary>
69         /// UpdateProgressChanged event. This event is occurred when a package is getting updated and the progress of the request to the package manager changes.
70         /// </summary>
71         public static event EventHandler<PackageManagerEventArgs> UpdateProgressChanged
72         {
73             add
74             {
75                 RegisterPackageManagerEventIfNeeded(GetFlaggedEventStatus(Interop.PackageManager.EventStatus.Upgrade));
76                 s_updateEventHandler += value;
77             }
78             remove
79             {
80                 s_updateEventHandler -= value;
81                 UnregisterPackageManagerEventIfNeeded(GetFlaggedEventStatus(Interop.PackageManager.EventStatus.Upgrade));
82             }
83         }
84
85         /// <summary>
86         /// Gets the package ID for the given app ID.
87         /// </summary>
88         /// <param name="applicationId">The ID of the application</param>
89         /// <returns>Returns the ID of the package. Empty string if App ID does not exist</returns>
90         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method</exception>
91         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access this method</exception>
92         /// <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
93         public static string GetPackageIdByApplicationId(string applicationId)
94         {
95             string packageId = string.Empty;
96             var err = Interop.PackageManager.PackageManageGetPackageIdByAppId(applicationId, out packageId);
97             if (err != Interop.PackageManager.ErrorCode.None)
98             {
99                 Log.Warn(LogTag, string.Format("Failed to get package Id. err = {0}", err));
100                 if (err != Interop.PackageManager.ErrorCode.InvalidParameter)
101                 {
102                     throw PackageManagerErrorFactory.GetException(err, "Failed to get package Id");
103                 }
104             }
105             return packageId;
106         }
107
108         /// <summary>
109         /// Gets the package information for the given package.
110         /// </summary>
111         /// <param name="packageId">The ID of the package</param>
112         /// <returns>Returns the package information for the given package ID.</returns>
113         /// <exception cref="ArgumentException">Thrown when failed when input package ID is invalid</exception>
114         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method</exception>
115         /// <exception cref="System.IO.IOException">Thrown when method failed due to internal IO error</exception>
116         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access this method</exception>
117         /// <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
118         public static Package GetPackage(string packageId)
119         {
120             return Package.GetPackage(packageId);
121         }
122
123         /// <summary>
124         /// Clears all application's internal and external cache directory.
125         /// </summary>
126         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method</exception>
127         /// <exception cref="System.IO.IOException">Thrown when method failed due to internal IO error</exception>
128         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access this method</exception>
129         /// <exception cref="SystemException">Thrown when method failed due to internal system error</exception>
130         /// <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
131         public static void ClearAllCacheDirectory()
132         {
133             var err = Interop.PackageManager.PackageManagerClearAllCacheDir();
134             if (err != Interop.PackageManager.ErrorCode.None)
135             {
136                 Log.Warn(LogTag, string.Format("Failed to clear all cache directories. err = {0}", err));
137                 throw PackageManagerErrorFactory.GetException(err, "Failed to clear all cache directories");
138             }
139         }
140
141         /// <summary>
142         /// Retrieves package information of all packages satisfying filter conditions.
143         /// </summary>
144         /// <returns>Returns the list of packages asynchronously.</returns>
145         /// <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
146         public static IEnumerable<Package> GetPackages()
147         {
148             return GetPackages(null);
149         }
150
151         /// <summary>
152         /// Retrieves package information of all packages satisfying filter conditions.
153         /// </summary>
154         /// <param name="filter">Optional - package filters</param>
155         /// <returns>Returns the list of packages asynchronously.</returns>
156         /// <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
157         public static IEnumerable<Package> GetPackages(PackageFilter filter)
158         {
159             List<Package> packageList = new List<Package>();
160
161             IntPtr filterHandle;
162             var err = Interop.PackageManager.PackageManagerFilterCreate(out filterHandle);
163             if (err != Interop.PackageManager.ErrorCode.None)
164             {
165                 Log.Warn(LogTag, string.Format("Failed to create package filter handle. err = {0}", err));
166                 return packageList;
167             }
168
169             if (filter != null && filter.Filters != null)
170             {
171                 foreach (KeyValuePair<string, bool> entry in filter?.Filters)
172                 {
173                     err = Interop.PackageManager.PackageManagerFilterAdd(filterHandle, entry.Key, entry.Value);
174                     if (err != Interop.PackageManager.ErrorCode.None)
175                     {
176                         Log.Warn(LogTag, string.Format("Failed to configure package filter. err = {0}", err));
177                         break;
178                     }
179                 }
180             }
181
182             if (err == Interop.PackageManager.ErrorCode.None)
183             {
184                 Interop.PackageManager.PackageManagerPackageInfoCallback cb = (handle, userData) =>
185                 {
186                     packageList.Add(Package.GetPackage(handle));
187                     return true;
188                 };
189
190                 err = Interop.PackageManager.PackageManagerFilterForeachPackageInfo(filterHandle, cb, IntPtr.Zero);
191                 if (err != Interop.PackageManager.ErrorCode.None)
192                 {
193                     Log.Warn(LogTag, string.Format("Failed to get package Informations. err = {0}", err));
194                 }
195             }
196
197             err = Interop.PackageManager.PackageManagerFilterDestroy(filterHandle);
198             if (err != Interop.PackageManager.ErrorCode.None)
199             {
200                 Log.Warn(LogTag, string.Format("Failed to destroy package filter handle. err = {0}", err));
201             }
202             return packageList;
203         }
204
205         /// <summary>
206         /// Gets the total package size information.
207         /// </summary>
208         /// <returns>Returns the total package size information asynchronously.</returns>
209         /// <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
210         public static async Task<PackageSizeInformation> GetTotalSizeInformationAsync()
211         {
212             TaskCompletionSource<PackageSizeInformation> tcs = new TaskCompletionSource<PackageSizeInformation>();
213             Interop.PackageManager.PackageManagerTotalSizeInfoCallback cb = (handle, userData) =>
214             {
215                 if (handle != IntPtr.Zero)
216                 {
217                     tcs.TrySetResult(PackageSizeInformation.GetPackageSizeInformation(handle));
218                 }
219             };
220
221             var err = Interop.PackageManager.PackageManagerGetTotalSizeInfo(cb, IntPtr.Zero);
222             if (err != Interop.PackageManager.ErrorCode.None)
223             {
224                 tcs.TrySetException(PackageManagerErrorFactory.GetException(err, "Failed to get total package size info"));
225             }
226             return await tcs.Task.ConfigureAwait(false);
227         }
228
229         /// <summary>
230         /// Installs package located at the given path
231         /// </summary>
232         /// <param name="packagePath">Absolute path for the package to be installed</param>
233         /// <returns>Returns true if installtion is successful, false otherwise.</returns>
234         /// <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
235         public static bool Install(string packagePath)
236         {
237             return Install(packagePath, null);
238         }
239
240         /// <summary>
241         /// Installs package located at the given path
242         /// </summary>
243         /// <param name="packagePath">Absolute path for the package to be installed</param>
244         /// <param name="expansionPackagePath">Optional - Absolute path for the expansion package to be installed</param>
245         /// <returns>Returns true if installtion is successful, false otherwise.</returns>
246         /// <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
247         public static bool Install(string packagePath, string expansionPackagePath)
248         {
249             IntPtr requestHandle;
250             var err = Interop.PackageManager.PackageManagerRequestCreate(out requestHandle);
251             if (err != Interop.PackageManager.ErrorCode.None)
252             {
253                 Log.Warn(LogTag, string.Format("Failed to install package. Error in creating request handle. err = {0}", err));
254                 return false;
255             }
256
257             if (!string.IsNullOrEmpty(expansionPackagePath))
258             {
259                 err = Interop.PackageManager.PackageManagerRequestSetTepPath(requestHandle, expansionPackagePath);
260                 if (err != Interop.PackageManager.ErrorCode.None)
261                 {
262                     Log.Warn(LogTag, string.Format("Failed to install package. Error in setting request package mode. err = {0}", err));
263                     Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
264                     return false;
265                 }
266             }
267
268             int requestId;
269             err = Interop.PackageManager.PackageManagerRequestInstall(requestHandle, packagePath, out requestId);
270             if (err != Interop.PackageManager.ErrorCode.None)
271             {
272                 Log.Warn(LogTag, string.Format("Failed to install package. err = {0}", err));
273                 Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
274                 return false;
275             }
276
277             err = Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
278             if (err != Interop.PackageManager.ErrorCode.None)
279             {
280                 Log.Warn(LogTag, string.Format("Failed to destroy package manager request handle. err = {0}", err));
281                 // This method returns true if all other operations are completed and error occured in destroying request handle.
282             }
283             return true;
284         }
285
286         /// <summary>
287         /// Uninstalls package with the given name.
288         /// </summary>
289         /// <param name="packageId">Id of the package to be uninstalled</param>
290         /// <param name="type">Package type for the package to be uninstalled</param>
291         /// <returns>Returns true if installtion is successful, false otherwise.</returns>
292         /// <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
293         public static bool Uninstall(string packageId, PackageType type)
294         {
295             IntPtr requestHandle;
296             var err = Interop.PackageManager.PackageManagerRequestCreate(out requestHandle);
297             if (err != Interop.PackageManager.ErrorCode.None)
298             {
299                 Log.Warn(LogTag, string.Format("Failed to uninstall package. Error in creating request handle. err = {0}", err));
300                 return false;
301             }
302
303             err = Interop.PackageManager.PackageManagerRequestSetType(requestHandle, type.ToString().ToLower());
304             if (err != Interop.PackageManager.ErrorCode.None)
305             {
306                 Log.Warn(LogTag, string.Format("Failed to uninstall package. Error in setting request package type. err = {0}", err));
307                 Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
308                 return false;
309             }
310
311             int requestId;
312             err = Interop.PackageManager.PackageManagerRequestUninstall(requestHandle, packageId, out requestId);
313             if (err != Interop.PackageManager.ErrorCode.None)
314             {
315                 Log.Warn(LogTag, string.Format("Failed to uninstall package. err = {0}", err));
316                 Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
317                 return false;
318             }
319
320             err = Interop.PackageManager.PackageManagerRequestDestroy(requestHandle);
321             if (err != Interop.PackageManager.ErrorCode.None)
322             {
323                 Log.Warn(LogTag, string.Format("Failed to destroy package manager request handle. err = {0}", err));
324                 // This method returns true if all other operations are completed and error occured in destroying request handle.
325             }
326             return true;
327         }
328
329         /// <summary>
330         /// Move package to given storage.
331         /// </summary>
332         /// <param name="packageId">Id of the package to be moved</param>
333         /// <param name="type">Package type for the package to be moved</param>
334         /// <param name="newStorage">Storage, package should be moved to</param>
335         /// <returns>Returns true if installtion is successful, false otherwise.</returns>
336         /// <privilege>http://tizen.org/privilege/packagemanager.admin</privilege>
337         public static bool Move(string packageId, PackageType type, StorageType newStorage)
338         {
339             IntPtr request;
340             Interop.PackageManager.ErrorCode err = Interop.PackageManager.PackageManagerRequestCreate(out request);
341             if (err != Interop.PackageManager.ErrorCode.None)
342             {
343                 Log.Warn(LogTag, string.Format("Failed to create request handle. err = {0}", err));
344                 return false;
345             }
346
347             err = Interop.PackageManager.PackageManagerRequestSetType(request, type.ToString().ToLower());
348             if (err != Interop.PackageManager.ErrorCode.None)
349             {
350                 Log.Warn(LogTag, string.Format("Failed to move package. Error in setting request package type. err = {0}", err));
351                 Interop.PackageManager.PackageManagerRequestDestroy(request);
352                 return false;
353             }
354
355             bool result = true;
356             err = Interop.PackageManager.PackageManagerRequestMove(request, packageId, (Interop.PackageManager.StorageType)newStorage);
357             if (err != Interop.PackageManager.ErrorCode.None)
358             {
359                 Log.Warn(LogTag, string.Format("Failed to move package to requested location. err = {0}", err));
360                 result = false;
361             }
362
363             err = Interop.PackageManager.PackageManagerRequestDestroy(request);
364             if (err != Interop.PackageManager.ErrorCode.None)
365             {
366                 Log.Warn(LogTag, string.Format("Failed to destroy package manager request handle. err = {0}", err));
367                 // This method returns true if all other operations are completed and error occured in destroying request handle.
368             }
369
370             return result;
371         }
372
373         private static Interop.PackageManager.EventStatus GetFlaggedEventStatus(Interop.PackageManager.EventStatus newEvent)
374         {
375             return s_eventStatus | newEvent;
376         }
377
378         private static Interop.PackageManager.EventStatus GetUnflaggedEventStatus(Interop.PackageManager.EventStatus oldEvent)
379         {
380             return s_eventStatus & (~oldEvent);
381         }
382
383         private static void RegisterPackageManagerEventIfNeeded(Interop.PackageManager.EventStatus eventStatus)
384         {
385             if (s_installEventHandler != null || s_uninstallEventHandler != null || s_updateEventHandler != null)
386             {
387                 return;
388             }
389
390             var err = Interop.PackageManager.ErrorCode.None;
391             s_packageManagerEventCallback = (packageType, packageId, eventType, eventState, progress, error, user_data) =>
392             {
393                 try
394                 {
395                     PackageManagerEventArgs eventArgs = new PackageManagerEventArgs(packageType, packageId, (PackageEventState)eventState, progress);
396                     if (eventType == Interop.PackageManager.EventType.Install)
397                     {
398                         s_installEventHandler?.Invoke(null, eventArgs);
399                     }
400                     else if (eventType == Interop.PackageManager.EventType.Uninstall)
401                     {
402                         s_uninstallEventHandler?.Invoke(null, eventArgs);
403                     }
404                     else if (eventType == Interop.PackageManager.EventType.Update)
405                     {
406                         s_updateEventHandler?.Invoke(null, eventArgs);
407                     }
408                 }
409                 catch (Exception e)
410                 {
411                     Log.Warn(LogTag, e.Message);
412                 }
413             };
414
415             if (s_handle.IsInvalid)
416             {
417                 err = Interop.PackageManager.PackageManagerCreate(out s_handle);
418                 if (err != Interop.PackageManager.ErrorCode.None)
419                 {
420                     Log.Warn(LogTag, string.Format("Failed to create package manager handle. err = {0}", err));
421                 }
422             }
423
424             if (!s_handle.IsInvalid)
425             {
426                 if (s_eventStatus != eventStatus)
427                 {
428                     err = Interop.PackageManager.PackageManagerSetEvenStatus(s_handle, eventStatus);
429                     if (err == Interop.PackageManager.ErrorCode.None)
430                     {
431                         s_eventStatus = eventStatus;
432                     }
433                 }
434                 err = Interop.PackageManager.PackageManagerSetEvent(s_handle, s_packageManagerEventCallback, IntPtr.Zero);
435             }
436             if (err != Interop.PackageManager.ErrorCode.None)
437             {
438                 Log.Warn(LogTag, string.Format("Failed to register callback for package manager event. err = {0}", err));
439             }
440         }
441
442         private static void UnregisterPackageManagerEventIfNeeded(Interop.PackageManager.EventStatus eventStatus)
443         {
444             if (s_installEventHandler != null || s_uninstallEventHandler != null || s_updateEventHandler != null)
445             {
446                 return;
447             }
448
449             if (s_handle.IsInvalid)
450             {
451                 return;
452             }
453
454             var err = Interop.PackageManager.PackageManagerUnsetEvent(s_handle);
455             if (s_eventStatus != eventStatus)
456             {
457                 err = Interop.PackageManager.PackageManagerSetEvenStatus(s_handle, eventStatus);
458                 if (err == Interop.PackageManager.ErrorCode.None)
459                 {
460                     s_eventStatus = eventStatus;
461                 }
462             }
463             if (err != Interop.PackageManager.ErrorCode.None)
464             {
465                 throw PackageManagerErrorFactory.GetException(err, "Failed to unregister package manager event event.");
466             }
467         }
468     }
469
470     internal static class PackageManagerErrorFactory
471     {
472         internal static Exception GetException(Interop.PackageManager.ErrorCode err, string message)
473         {
474             string errMessage = string.Format("{0} err = {1}", message, err);
475             switch (err)
476             {
477                 case Interop.PackageManager.ErrorCode.InvalidParameter:
478                 case Interop.PackageManager.ErrorCode.NoSuchPackage:
479                     return new ArgumentException(errMessage);
480                 case Interop.PackageManager.ErrorCode.PermissionDenied:
481                     return new UnauthorizedAccessException(errMessage);
482                 case Interop.PackageManager.ErrorCode.IoError:
483                     return new System.IO.IOException(errMessage);
484                 default:
485                     return new InvalidOperationException(errMessage);
486             }
487         }
488     }
489 }