2 * Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
20 using System.Threading.Tasks;
22 namespace Tizen.Security
25 /// The PrivacyPrivilegeManager provides the properties or methods to check and request a permission for privacy privilege.
27 /// <since_tizen> 4 </since_tizen>
28 public static class PrivacyPrivilegeManager
30 private const string LogTag = "Tizen.Privilege";
31 private static Dictionary<int, TaskCompletionSource<RequestMultipleResponseEventArgs>> s_multipleRequestMap = new Dictionary<int, TaskCompletionSource<RequestMultipleResponseEventArgs>>();
32 private static int s_requestId = 0;
33 private static IDictionary<string, WeakReference<ResponseContext>> s_responseWeakMap = new Dictionary<string, WeakReference<ResponseContext>>();
34 private static Interop.PrivacyPrivilegeManager.RequestResponseCallback s_requestResponseCb =
35 (Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult result,
36 string privilege, IntPtr userData) =>
40 if (s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef))
42 if (weakRef.TryGetTarget(out ResponseContext context))
44 context.FireEvent((CallCause)cause, (RequestResult)result);
48 s_responseWeakMap.Remove(privilege);
49 Log.Error(LogTag, "No response context for: " + privilege);
54 Log.Error(LogTag, "No listener for: " + privilege);
59 Log.Error(LogTag, "Exception in callback : " + e.Message);
61 s_PrivilegesInProgress.Remove(privilege);
64 private static IDictionary<string, ResponseContext> s_responseMap = new Dictionary<string, ResponseContext>();
65 private static HashSet<string> s_PrivilegesInProgress = new HashSet<string>();
66 private static Interop.PrivacyPrivilegeManager.RequestMultipleResponseCallback s_multipleCallback = MultipleRequestHandler;
68 private static string[] CheckPrivilegesArgument(IEnumerable<string> privileges, string methodName)
70 if (privileges == null || !privileges.Any())
72 Log.Error(LogTag, "privileges for " + methodName + " are null or empty.");
73 throw new ArgumentException("privileges for " + methodName + " are null or empty.");
76 foreach (var privilege in privileges)
78 if (string.IsNullOrEmpty(privilege))
80 Log.Error(LogTag, " At least one privilege for " + methodName + " is null or empty.");
81 throw new ArgumentException(" At least one privilege for " + methodName + " is null or empty.");
85 return privileges as string[] ?? privileges.ToArray();
89 /// Gets the status of a privacy privilege permission.
91 /// <param name="privilege">The privacy privilege to be checked.</param>
92 /// <returns>The permission setting for a respective privilege.</returns>
93 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
94 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
95 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
98 /// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
102 /// // Privilege can be used
105 /// // Privilege can't be used
108 /// // User permission request required
109 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
114 /// <since_tizen> 4 </since_tizen>
115 public static CheckResult CheckPermission(string privilege)
117 Interop.PrivacyPrivilegeManager.CheckResult result;
118 int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermission(privilege, out result);
119 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
121 Log.Error(LogTag, "Failed to check permission");
122 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
124 return (CheckResult)result;
128 /// Gets the status of a privacy privileges permission.
130 /// <param name="privileges">The privacy privileges to be checked.</param>
131 /// <returns>The permission setting for a respective privileges.</returns>
132 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
133 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
134 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
137 /// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
138 /// "http://tizen.org/privilege/alarm"};
139 /// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
140 /// List<string> privilegesWithAskStatus = new List<string>();
141 /// for (int iterator = 0; iterator < results.Length; ++iterator)
143 /// switch (results[iterator])
145 /// case CheckResult.Allow:
146 /// // Privilege can be used
148 /// case CheckResult.Deny:
149 /// // Privilege can't be used
151 /// case CheckResult.Ask:
152 /// // User permission request required
153 /// privilegesWithAskStatus.Add(privileges[iterator]);
157 /// PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus);
160 /// <since_tizen> 6 </since_tizen>
161 public static IEnumerable<CheckResult> CheckPermissions(IEnumerable<string> privileges)
163 string[] privilegesArray = CheckPrivilegesArgument(privileges, "CheckPermissions");
165 Interop.PrivacyPrivilegeManager.CheckResult[] results = new Interop.PrivacyPrivilegeManager.CheckResult[privilegesArray.Length];
166 int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermissions(privilegesArray, (uint)privilegesArray.Length, results);
167 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
169 Log.Error(LogTag, "Failed to check permission");
170 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
173 CheckResult[] checkResults = new CheckResult[results.Length];
174 for (int iterator = 0; iterator < results.Length; ++iterator)
176 checkResults[iterator] = (CheckResult)results[iterator];
183 /// Triggers the permission request for a user.
185 /// <param name="privilege">The privacy privilege to be requested.</param>
186 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
187 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
188 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
191 /// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
195 /// // Privilege can be used
198 /// // Privilege can't be used
201 /// // User permission request required
202 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
207 /// <since_tizen> 4 </since_tizen>
208 public static void RequestPermission(string privilege)
210 if (!s_PrivilegesInProgress.Add(privilege))
212 Log.Error(LogTag, "Request for this privilege: " + privilege + " is already in progress.");
213 throw new ArgumentException("Request for this privilege: " + privilege + " is already in progress.");
216 int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermission(privilege, s_requestResponseCb, IntPtr.Zero);
217 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
219 Log.Error(LogTag, "Failed to request permission");
220 s_PrivilegesInProgress.Remove(privilege);
221 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
226 /// Triggers the permissions request for a user.
228 /// <param name="privileges">The privacy privileges to be requested.</param>
229 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
230 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
231 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
232 /// <returns>Permission request Task</returns>
235 /// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
236 /// "http://tizen.org/privilege/alarm"};
237 /// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
238 /// List<string> privilegesWithAskStatus = new List<string>();
239 /// for (int iterator = 0; iterator < results.Length; ++iterator)
241 /// switch (results[iterator])
243 /// case CheckResult.Allow:
244 /// // Privilege can be used
246 /// case CheckResult.Deny:
247 /// // Privilege can't be used
249 /// case CheckResult.Ask:
250 /// // User permission request required
251 /// privilegesWithAskStatus.Add(privileges[iterator]);
255 /// IEnumerable<PermissionRequestResponse> responses = PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus).Result;
256 /// //handle responses
259 /// <since_tizen> 6 </since_tizen>
260 public static Task<RequestMultipleResponseEventArgs> RequestPermissions(IEnumerable<string> privileges)
262 string[] privilegesArray = CheckPrivilegesArgument(privileges, "RequestPermissions");
264 for (int iterator = 0; iterator < privilegesArray.Length; ++iterator)
266 if (!s_PrivilegesInProgress.Add(privilegesArray[iterator]))
268 Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
270 for (int removeIterator = iterator - 1; removeIterator >= 0; --removeIterator)
272 s_PrivilegesInProgress.Remove(privilegesArray[removeIterator]);
274 Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
275 throw new ArgumentException("Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
279 Log.Info(LogTag, "Sending request for permissions: " + string.Join(" ", privilegesArray));
282 lock (s_multipleRequestMap)
284 requestId = s_requestId++;
286 TaskCompletionSource<RequestMultipleResponseEventArgs> permissionResponsesTask = new TaskCompletionSource<RequestMultipleResponseEventArgs>();
287 s_multipleRequestMap[requestId] = permissionResponsesTask;
288 int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermissions(privilegesArray, (uint)privilegesArray.Length, s_multipleCallback, (IntPtr)requestId);
290 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
292 Log.Error(LogTag, "Failed to request permissions.");
293 foreach (string privilege in privileges)
295 s_PrivilegesInProgress.Remove(privilege);
297 s_multipleRequestMap.Remove(requestId);
298 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
302 Log.Info(LogTag, "Requesting permissions successfull.");
303 return permissionResponsesTask.Task;
308 /// Gets the response context for a given privilege.
310 /// <seealso cref="ResponseContext"/>
311 /// <param name="privilege">The privilege.</param>
312 /// <returns>The response context of a respective privilege.</returns>
313 /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
316 /// private static void PPM_RequestResponse(object sender, RequestResponseEventArgs e)
318 /// if (e.cause == CallCause.Answer)
324 /// case RequestResult.AllowForever:
325 /// Console.WriteLine("User allowed usage of privilege {0} definitely", e.privilege);
327 /// case RequestResult.DenyForever:
328 /// Console.WriteLine("User denied usage of privilege {0} definitely", e.privilege);
330 /// case RequestResult.DenyOnce:
331 /// Console.WriteLine("User denied usage of privilege {0} this time", e.privilege);
337 /// Console.WriteLine("Error occured during requesting permission for {0}", e.privilege);
341 /// PrivacyPrivilegeManager.ResponseContext context = null;
342 /// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
343 /// if(context != null)
345 /// context.ResponseFetched += PPM_RequestResponse;
348 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
350 /// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
351 /// if(context != null)
353 /// context.ResponseFetched -= PPM_RequestResponse;
357 /// <since_tizen> 4 </since_tizen>
358 public static WeakReference<ResponseContext> GetResponseContext(string privilege)
360 if (!(s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef) && weakRef.TryGetTarget(out ResponseContext context)))
362 context = new ResponseContext(privilege);
363 s_responseWeakMap[privilege] = new WeakReference<ResponseContext>(context);
365 return s_responseWeakMap[privilege];
368 private static void MultipleRequestHandler(Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult[] results,
369 string[] requestedPrivileges, uint privilegesCount, IntPtr userData)
371 int requestId = (int)userData;
372 if (!s_multipleRequestMap.ContainsKey(requestId))
377 var tcs = s_multipleRequestMap[requestId];
378 RequestMultipleResponseEventArgs requestResponse = new RequestMultipleResponseEventArgs();
379 PermissionRequestResponse[] permissionResponses = new PermissionRequestResponse[privilegesCount];
381 for (int iterator = 0; iterator < privilegesCount; ++iterator)
383 permissionResponses[iterator] = new PermissionRequestResponse
385 Privilege = requestedPrivileges[iterator],
386 Result = (RequestResult)results[iterator]
389 requestResponse.Cause = (CallCause)cause;
390 requestResponse.Responses = permissionResponses;
392 foreach (string privilege in requestedPrivileges)
394 s_PrivilegesInProgress.Remove(privilege);
396 tcs.SetResult(requestResponse);
397 s_multipleRequestMap.Remove(requestId);
401 /// This class manages event handlers of the privilege permission requests.
402 /// This class enables having event handlers for an individual privilege.
404 /// <since_tizen> 4 </since_tizen>
405 public class ResponseContext
407 private string _privilege;
409 internal ResponseContext(string privilege)
411 _privilege = privilege;
414 /// Occurs when the response for a permission request is fetched.
416 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
417 /// <since_tizen> 4 </since_tizen>
418 public event EventHandler<RequestResponseEventArgs> ResponseFetched
422 if (_ResponseFetched == null)
424 if (!s_responseMap.ContainsKey(_privilege))
426 s_responseMap[_privilege] = this;
429 _ResponseFetched += value;
434 _ResponseFetched -= value;
435 if (_ResponseFetched == null)
437 if (s_responseMap.ContainsKey(_privilege))
439 s_responseMap.Remove(_privilege);
445 private event EventHandler<RequestResponseEventArgs> _ResponseFetched;
447 internal void FireEvent(CallCause _cause, RequestResult _result)
449 _ResponseFetched?.Invoke(this, new RequestResponseEventArgs { cause = _cause, result = _result, privilege = _privilege });
454 internal static class PrivacyPrivilegeManagerErrorFactory
456 static internal Exception GetException(int error)
458 Interop.PrivacyPrivilegeManager.ErrorCode errCode = (Interop.PrivacyPrivilegeManager.ErrorCode)error;
461 case Interop.PrivacyPrivilegeManager.ErrorCode.InvalidParameter:
462 return new ArgumentException("Invalid parameter");
463 case Interop.PrivacyPrivilegeManager.ErrorCode.IoError:
464 return new System.IO.IOException("I/O Error");
465 case Interop.PrivacyPrivilegeManager.ErrorCode.OutOfMemory:
466 return new OutOfMemoryException("Out of memory");
467 case Interop.PrivacyPrivilegeManager.ErrorCode.Unknown:
469 return new ArgumentException("Unknown error");