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;
21 using Tizen.Internals.Errors;
23 namespace Tizen.Security
26 /// The PrivacyPrivilegeManager provides the properties or methods to check and request a permission for privacy privilege.
28 /// <since_tizen> 4 </since_tizen>
29 public static class PrivacyPrivilegeManager
31 private const string LogTag = "Tizen.Privilege";
32 private static IDictionary<string, WeakReference<ResponseContext>> s_responseWeakMap = new Dictionary<string, WeakReference<ResponseContext>>();
33 private static Interop.PrivacyPrivilegeManager.RequestResponseCallback s_requestResponseCb =
34 (Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult result,
35 string privilege, IntPtr userData) =>
39 if (s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef))
41 if (weakRef.TryGetTarget(out ResponseContext context))
43 context.FireEvent((CallCause)cause, (RequestResult)result);
47 s_responseWeakMap.Remove(privilege);
48 Log.Error(LogTag, "No response context for: " + privilege);
53 Log.Error(LogTag, "No listener for: " + privilege);
58 Log.Error(LogTag, "Exception in callback : " + e.Message);
60 s_PrivilegesInProgress.Remove(privilege);
63 private static IDictionary<string, ResponseContext> s_responseMap = new Dictionary<string, ResponseContext>();
64 private static HashSet<string> s_PrivilegesInProgress = new HashSet<string>();
66 private static string[] CheckPrivilegesArgument(IEnumerable<string> privileges, string methodName)
68 if (privileges == null || !privileges.Any())
70 Log.Error(LogTag, "privileges for " + methodName + " are null or empty.");
71 throw new ArgumentException("privileges for " + methodName + " are null or empty.");
74 foreach (var privilege in privileges)
76 if (string.IsNullOrEmpty(privilege))
78 Log.Error(LogTag, " At least one privilege for " + methodName + " is null or empty.");
79 throw new ArgumentException(" At least one privilege for " + methodName + " is null or empty.");
83 return privileges as string[] ?? privileges.ToArray();
87 /// Gets the status of a privacy privilege permission.
89 /// <param name="privilege">The privacy privilege to be checked.</param>
90 /// <returns>The permission setting for a respective privilege.</returns>
91 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
92 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
93 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
96 /// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
100 /// // Privilege can be used
103 /// // Privilege can't be used
106 /// // User permission request required
107 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
112 /// <since_tizen> 4 </since_tizen>
113 public static CheckResult CheckPermission(string privilege)
115 Interop.PrivacyPrivilegeManager.CheckResult result;
116 int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermission(privilege, out result);
117 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
119 Log.Error(LogTag, "Failed to check permission");
120 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
122 return (CheckResult)result;
126 /// Gets the status of a privacy privileges permission.
128 /// <param name="privileges">The privacy privileges to be checked.</param>
129 /// <returns>The permission setting for a respective privileges.</returns>
130 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
131 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
132 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
135 /// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
136 /// "http://tizen.org/privilege/alarm"};
137 /// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
138 /// List<string> privilegesWithAskStatus = new List<string>();
139 /// for (int iterator = 0; iterator < results.Length; ++iterator)
141 /// switch (results[iterator])
143 /// case CheckResult.Allow:
144 /// // Privilege can be used
146 /// case CheckResult.Deny:
147 /// // Privilege can't be used
149 /// case CheckResult.Ask:
150 /// // User permission request required
151 /// privilegesWithAskStatus.Add(privileges[iterator]);
155 /// PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus);
158 /// <since_tizen> 6 </since_tizen>
159 public static IEnumerable<CheckResult> CheckPermissions(IEnumerable<string> privileges)
161 string[] privilegesArray = CheckPrivilegesArgument(privileges, "CheckPermissions");
163 Interop.PrivacyPrivilegeManager.CheckResult[] results = new Interop.PrivacyPrivilegeManager.CheckResult[privilegesArray.Length];
164 int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermissions(privilegesArray, (uint)privilegesArray.Length, results);
165 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
167 Log.Error(LogTag, "Failed to check permission");
168 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
171 CheckResult[] checkResults = new CheckResult[results.Length];
172 for (int iterator = 0; iterator < results.Length; ++iterator)
174 checkResults[iterator] = (CheckResult)results[iterator];
181 /// Triggers the permission request for a user.
183 /// <param name="privilege">The privacy privilege to be requested.</param>
184 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
185 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
186 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
189 /// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
193 /// // Privilege can be used
196 /// // Privilege can't be used
199 /// // User permission request required
200 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
205 /// <since_tizen> 4 </since_tizen>
206 public static void RequestPermission(string privilege)
208 if (!s_PrivilegesInProgress.Add(privilege))
210 Log.Error(LogTag, "Request for this privilege: " + privilege + " is already in progress.");
211 throw new ArgumentException("Request for this privilege: " + privilege + " is already in progress.");
214 int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermission(privilege, s_requestResponseCb, IntPtr.Zero);
215 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
217 Log.Error(LogTag, "Failed to request permission");
218 s_PrivilegesInProgress.Remove(privilege);
219 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
224 /// Triggers the permissions request for a user.
226 /// <param name="privileges">The privacy privileges to be requested.</param>
227 /// <exception cref="ArgumentException">Thrown when an invalid parameter is passed.</exception>
228 /// <exception cref="OutOfMemoryException">Thrown when a memory error occurred.</exception>
229 /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
230 /// <returns>Permission request Task</returns>
233 /// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
234 /// "http://tizen.org/privilege/alarm"};
235 /// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
236 /// List<string> privilegesWithAskStatus = new List<string>();
237 /// for (int iterator = 0; iterator < results.Length; ++iterator)
239 /// switch (results[iterator])
241 /// case CheckResult.Allow:
242 /// // Privilege can be used
244 /// case CheckResult.Deny:
245 /// // Privilege can't be used
247 /// case CheckResult.Ask:
248 /// // User permission request required
249 /// privilegesWithAskStatus.Add(privileges[iterator]);
253 /// IEnumerable<PermissionRequestResponse> responses = PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus).Result;
254 /// //handle responses
257 /// <since_tizen> 6 </since_tizen>
258 public static Task<RequestMultipleResponseEventArgs> RequestPermissions(IEnumerable<string> privileges)
260 string[] privilegesArray = CheckPrivilegesArgument(privileges, "RequestPermissions");
262 for (int iterator = 0; iterator < privilegesArray.Length; ++iterator)
264 if (!s_PrivilegesInProgress.Add(privilegesArray[iterator]))
266 Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
268 for (int removeIterator = iterator - 1; removeIterator >= 0; --removeIterator)
270 s_PrivilegesInProgress.Remove(privilegesArray[removeIterator]);
272 Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
273 throw new ArgumentException("Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
277 Log.Info(LogTag, "Sending request for permissions: " + string.Join(" ", privilegesArray));
279 TaskCompletionSource<RequestMultipleResponseEventArgs> permissionResponsesTask = new TaskCompletionSource<RequestMultipleResponseEventArgs>();
280 int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermissions(privilegesArray, (uint)privilegesArray.Length,
281 (Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult[] results,
282 string[] requestedPrivileges, uint privilegesCount, IntPtr userData) =>
284 Log.Info(LogTag, "Sending request for permissions: ");
285 RequestMultipleResponseEventArgs requestResponse = new RequestMultipleResponseEventArgs();
286 PermissionRequestResponse[] permissionResponses = new PermissionRequestResponse[privilegesCount];
288 for (int iterator = 0; iterator < privilegesCount; ++iterator)
290 permissionResponses[iterator] = new PermissionRequestResponse
292 Privilege = requestedPrivileges[iterator],
293 Result = (RequestResult)results[iterator]
296 requestResponse.Cause = (CallCause)cause;
297 requestResponse.Responses = permissionResponses;
299 foreach (string privilege in requestedPrivileges)
301 s_PrivilegesInProgress.Remove(privilege);
303 permissionResponsesTask.SetResult(requestResponse);
306 if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
308 Log.Error(LogTag, "Failed to request permissions.");
309 foreach (string privilege in privileges)
311 s_PrivilegesInProgress.Remove(privilege);
313 throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
317 Log.Info(LogTag, "Requesting permissions successfull.");
318 return permissionResponsesTask.Task;
323 /// Gets the response context for a given privilege.
325 /// <seealso cref="ResponseContext"/>
326 /// <param name="privilege">The privilege.</param>
327 /// <returns>The response context of a respective privilege.</returns>
328 /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
331 /// private static void PPM_RequestResponse(object sender, RequestResponseEventArgs e)
333 /// if (e.cause == CallCause.Answer)
339 /// case RequestResult.AllowForever:
340 /// Console.WriteLine("User allowed usage of privilege {0} definitely", e.privilege);
342 /// case RequestResult.DenyForever:
343 /// Console.WriteLine("User denied usage of privilege {0} definitely", e.privilege);
345 /// case RequestResult.DenyOnce:
346 /// Console.WriteLine("User denied usage of privilege {0} this time", e.privilege);
352 /// Console.WriteLine("Error occured during requesting permission for {0}", e.privilege);
356 /// PrivacyPrivilegeManager.ResponseContext context = null;
357 /// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
358 /// if(context != null)
360 /// context.ResponseFetched += PPM_RequestResponse;
363 /// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
365 /// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
366 /// if(context != null)
368 /// context.ResponseFetched -= PPM_RequestResponse;
372 /// <since_tizen> 4 </since_tizen>
373 public static WeakReference<ResponseContext> GetResponseContext(string privilege)
375 if (!(s_responseWeakMap.TryGetValue(privilege, out WeakReference<ResponseContext> weakRef) && weakRef.TryGetTarget(out ResponseContext context)))
377 context = new ResponseContext(privilege);
378 s_responseWeakMap[privilege] = new WeakReference<ResponseContext>(context);
380 return s_responseWeakMap[privilege];
384 /// This class manages event handlers of the privilege permission requests.
385 /// This class enables having event handlers for an individual privilege.
387 /// <since_tizen> 4 </since_tizen>
388 public class ResponseContext
390 private string _privilege;
392 internal ResponseContext(string privilege)
394 _privilege = privilege;
397 /// Occurs when the response for a permission request is fetched.
399 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
400 /// <since_tizen> 4 </since_tizen>
401 public event EventHandler<RequestResponseEventArgs> ResponseFetched
405 if (_ResponseFetched == null)
407 if (!s_responseMap.ContainsKey(_privilege))
409 s_responseMap[_privilege] = this;
412 _ResponseFetched += value;
417 _ResponseFetched -= value;
418 if (_ResponseFetched == null)
420 if (s_responseMap.ContainsKey(_privilege))
422 s_responseMap.Remove(_privilege);
428 private event EventHandler<RequestResponseEventArgs> _ResponseFetched;
430 internal void FireEvent(CallCause _cause, RequestResult _result)
432 _ResponseFetched?.Invoke(this, new RequestResponseEventArgs { cause = _cause, result = _result, privilege = _privilege });
437 internal static class PrivacyPrivilegeManagerErrorFactory
439 static internal Exception GetException(int error)
441 Interop.PrivacyPrivilegeManager.ErrorCode errCode = (Interop.PrivacyPrivilegeManager.ErrorCode)error;
444 case Interop.PrivacyPrivilegeManager.ErrorCode.InvalidParameter:
445 return new ArgumentException("Invalid parameter");
446 case Interop.PrivacyPrivilegeManager.ErrorCode.IoError:
447 return new System.IO.IOException("I/O Error");
448 case Interop.PrivacyPrivilegeManager.ErrorCode.OutOfMemory:
449 return new OutOfMemoryException("Out of memory");
450 case Interop.PrivacyPrivilegeManager.ErrorCode.Unknown:
452 return new ArgumentException("Unknown error");