/*
* Copyright (c) 2017 - 2018 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.Threading.Tasks;
namespace Tizen.Security
{
///
/// The PrivacyPrivilegeManager provides the properties or methods to check and request a permission for privacy privilege.
///
/// 4
public static class PrivacyPrivilegeManager
{
private const string LogTag = "Tizen.Privilege";
private static Dictionary> s_multipleRequestMap = new Dictionary>();
private static int s_requestId = 0;
private static IDictionary> s_responseWeakMap = new Dictionary>();
private static Interop.PrivacyPrivilegeManager.RequestResponseCallback s_requestResponseCb =
(Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult result,
string privilege, IntPtr userData) =>
{
try
{
if (s_responseWeakMap.TryGetValue(privilege, out WeakReference weakRef))
{
if (weakRef.TryGetTarget(out ResponseContext context))
{
context.FireEvent((CallCause)cause, (RequestResult)result);
}
else
{
s_responseWeakMap.Remove(privilege);
Log.Error(LogTag, "No response context for: " + privilege);
}
}
else
{
Log.Error(LogTag, "No listener for: " + privilege);
}
}
catch (Exception e)
{
Log.Error(LogTag, "Exception in callback : " + e.Message);
}
s_PrivilegesInProgress.Remove(privilege);
};
private static IDictionary s_responseMap = new Dictionary();
private static HashSet s_PrivilegesInProgress = new HashSet();
private static Interop.PrivacyPrivilegeManager.RequestMultipleResponseCallback s_multipleCallback = MultipleRequestHandler;
private static string[] CheckPrivilegesArgument(IEnumerable privileges, string methodName)
{
if (privileges == null || !privileges.Any())
{
Log.Error(LogTag, "privileges for " + methodName + " are null or empty.");
throw new ArgumentException("privileges for " + methodName + " are null or empty.");
}
foreach (var privilege in privileges)
{
if (string.IsNullOrEmpty(privilege))
{
Log.Error(LogTag, " At least one privilege for " + methodName + " is null or empty.");
throw new ArgumentException(" At least one privilege for " + methodName + " is null or empty.");
}
}
return privileges as string[] ?? privileges.ToArray();
}
///
/// Gets the status of a privacy privilege permission.
///
/// The privacy privilege to be checked.
/// The permission setting for a respective privilege.
/// Thrown when an invalid parameter is passed.
/// Thrown when a memory error occurred.
/// Thrown when the method failed due to an internal I/O error.
///
///
/// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
/// switch (result)
/// {
/// case Allow:
/// // Privilege can be used
/// break;
/// case Deny:
/// // Privilege can't be used
/// break;
/// case Ask:
/// // User permission request required
/// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
/// break;
/// }
///
///
/// 4
public static CheckResult CheckPermission(string privilege)
{
Interop.PrivacyPrivilegeManager.CheckResult result;
int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermission(privilege, out result);
if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
{
Log.Error(LogTag, "Failed to check permission");
throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
}
return (CheckResult)result;
}
///
/// Gets the status of a privacy privileges permission.
///
/// The privacy privileges to be checked.
/// The permission setting for a respective privileges.
/// Thrown when an invalid parameter is passed.
/// Thrown when a memory error occurred.
/// Thrown when the method failed due to an internal I/O error.
///
///
/// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
/// "http://tizen.org/privilege/alarm"};
/// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
/// List<string> privilegesWithAskStatus = new List<string>();
/// for (int iterator = 0; iterator < results.Length; ++iterator)
/// {
/// switch (results[iterator])
/// {
/// case CheckResult.Allow:
/// // Privilege can be used
/// break;
/// case CheckResult.Deny:
/// // Privilege can't be used
/// break;
/// case CheckResult.Ask:
/// // User permission request required
/// privilegesWithAskStatus.Add(privileges[iterator]);
/// break;
/// }
/// }
/// PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus);
///
///
/// 6
public static IEnumerable CheckPermissions(IEnumerable privileges)
{
string[] privilegesArray = CheckPrivilegesArgument(privileges, "CheckPermissions");
Interop.PrivacyPrivilegeManager.CheckResult[] results = new Interop.PrivacyPrivilegeManager.CheckResult[privilegesArray.Length];
int ret = (int)Interop.PrivacyPrivilegeManager.CheckPermissions(privilegesArray, (uint)privilegesArray.Length, results);
if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
{
Log.Error(LogTag, "Failed to check permission");
throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
}
CheckResult[] checkResults = new CheckResult[results.Length];
for (int iterator = 0; iterator < results.Length; ++iterator)
{
checkResults[iterator] = (CheckResult)results[iterator];
}
return checkResults;
}
///
/// Triggers the permission request for a user.
///
/// The privacy privilege to be requested.
/// Thrown when an invalid parameter is passed.
/// Thrown when a memory error occurred.
/// Thrown when the method failed due to an internal I/O error.
///
///
/// CheckResult result = PrivacyPrivilegeManager.CheckPermission("http://tizen.org/privilege/account.read");
/// switch (result)
/// {
/// case Allow:
/// // Privilege can be used
/// break;
/// case Deny:
/// // Privilege can't be used
/// break;
/// case Ask:
/// // User permission request required
/// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
/// break;
/// }
///
///
/// 4
public static void RequestPermission(string privilege)
{
if (!s_PrivilegesInProgress.Add(privilege))
{
Log.Error(LogTag, "Request for this privilege: " + privilege + " is already in progress.");
throw new ArgumentException("Request for this privilege: " + privilege + " is already in progress.");
}
int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermission(privilege, s_requestResponseCb, IntPtr.Zero);
if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
{
Log.Error(LogTag, "Failed to request permission");
s_PrivilegesInProgress.Remove(privilege);
throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
}
}
///
/// Triggers the permissions request for a user.
///
/// The privacy privileges to be requested.
/// Thrown when an invalid parameter is passed.
/// Thrown when a memory error occurred.
/// Thrown when the method failed due to an internal I/O error.
/// Permission request Task
///
///
/// string[] privileges = new [] {"http://tizen.org/privilege/account.read",
/// "http://tizen.org/privilege/alarm"};
/// CheckResult[] results = PrivacyPrivilegeManager.CheckPermissions(privileges).ToArray();
/// List<string> privilegesWithAskStatus = new List<string>();
/// for (int iterator = 0; iterator < results.Length; ++iterator)
/// {
/// switch (results[iterator])
/// {
/// case CheckResult.Allow:
/// // Privilege can be used
/// break;
/// case CheckResult.Deny:
/// // Privilege can't be used
/// break;
/// case CheckResult.Ask:
/// // User permission request required
/// privilegesWithAskStatus.Add(privileges[iterator]);
/// break;
/// }
/// }
/// IEnumerable<PermissionRequestResponse> responses = PrivacyPrivilegeManager.RequestPermissions(privilegesWithAskStatus).Result;
/// //handle responses
///
///
/// 6
public static Task RequestPermissions(IEnumerable privileges)
{
string[] privilegesArray = CheckPrivilegesArgument(privileges, "RequestPermissions");
for (int iterator = 0; iterator < privilegesArray.Length; ++iterator)
{
if (!s_PrivilegesInProgress.Add(privilegesArray[iterator]))
{
Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
for (int removeIterator = iterator - 1; removeIterator >= 0; --removeIterator)
{
s_PrivilegesInProgress.Remove(privilegesArray[removeIterator]);
}
Log.Error(LogTag, "Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
throw new ArgumentException("Request for this privilege: " + privilegesArray[iterator] + " is already in progress.");
}
}
Log.Info(LogTag, "Sending request for permissions: " + string.Join(" ", privilegesArray));
int requestId = 0;
lock (s_multipleRequestMap)
{
requestId = s_requestId++;
}
TaskCompletionSource permissionResponsesTask = new TaskCompletionSource();
s_multipleRequestMap[requestId] = permissionResponsesTask;
int ret = (int)Interop.PrivacyPrivilegeManager.RequestPermissions(privilegesArray, (uint)privilegesArray.Length, s_multipleCallback, (IntPtr)requestId);
if (ret != (int)Interop.PrivacyPrivilegeManager.ErrorCode.None)
{
Log.Error(LogTag, "Failed to request permissions.");
foreach (string privilege in privileges)
{
s_PrivilegesInProgress.Remove(privilege);
}
s_multipleRequestMap.Remove(requestId);
throw PrivacyPrivilegeManagerErrorFactory.GetException(ret);
}
else
{
Log.Info(LogTag, "Requesting permissions successfull.");
return permissionResponsesTask.Task;
}
}
///
/// Gets the response context for a given privilege.
///
///
/// The privilege.
/// The response context of a respective privilege.
/// Thrown if the key is an invalid parameter.
///
///
/// private static void PPM_RequestResponse(object sender, RequestResponseEventArgs e)
/// {
/// if (e.cause == CallCause.Answer)
/// {
/// switch(e.result)
///
/// {
///
/// case RequestResult.AllowForever:
/// Console.WriteLine("User allowed usage of privilege {0} definitely", e.privilege);
/// break;
/// case RequestResult.DenyForever:
/// Console.WriteLine("User denied usage of privilege {0} definitely", e.privilege);
/// break;
/// case RequestResult.DenyOnce:
/// Console.WriteLine("User denied usage of privilege {0} this time", e.privilege);
/// break;
/// };
/// }
/// else
/// {
/// Console.WriteLine("Error occured during requesting permission for {0}", e.privilege);
/// }
///}
///
/// PrivacyPrivilegeManager.ResponseContext context = null;
/// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
/// if(context != null)
/// {
/// context.ResponseFetched += PPM_RequestResponse;
/// }
///
/// PrivacyPrivilegeManager.RequestPermission("http://tizen.org/privilege/account.read");
///
/// PrivacyPrivilegeManager.GetResponseContext("http://tizen.org/privilege/account.read").TryGetTarget(out context);
/// if(context != null)
/// {
/// context.ResponseFetched -= PPM_RequestResponse;
/// }
///
///
/// 4
public static WeakReference GetResponseContext(string privilege)
{
if (!(s_responseWeakMap.TryGetValue(privilege, out WeakReference weakRef) && weakRef.TryGetTarget(out ResponseContext context)))
{
context = new ResponseContext(privilege);
s_responseWeakMap[privilege] = new WeakReference(context);
}
return s_responseWeakMap[privilege];
}
private static void MultipleRequestHandler(Interop.PrivacyPrivilegeManager.CallCause cause, Interop.PrivacyPrivilegeManager.RequestResult[] results,
string[] requestedPrivileges, uint privilegesCount, IntPtr userData)
{
int requestId = (int)userData;
if (!s_multipleRequestMap.ContainsKey(requestId))
{
return;
}
var tcs = s_multipleRequestMap[requestId];
RequestMultipleResponseEventArgs requestResponse = new RequestMultipleResponseEventArgs();
PermissionRequestResponse[] permissionResponses = new PermissionRequestResponse[privilegesCount];
for (int iterator = 0; iterator < privilegesCount; ++iterator)
{
permissionResponses[iterator] = new PermissionRequestResponse
{
Privilege = requestedPrivileges[iterator],
Result = (RequestResult)results[iterator]
};
}
requestResponse.Cause = (CallCause)cause;
requestResponse.Responses = permissionResponses;
foreach (string privilege in requestedPrivileges)
{
s_PrivilegesInProgress.Remove(privilege);
}
tcs.SetResult(requestResponse);
s_multipleRequestMap.Remove(requestId);
}
///
/// This class manages event handlers of the privilege permission requests.
/// This class enables having event handlers for an individual privilege.
///
/// 4
public class ResponseContext
{
private string _privilege;
internal ResponseContext(string privilege)
{
_privilege = privilege;
}
///
/// Occurs when the response for a permission request is fetched.
///
/// Thrown when the bundle instance has been disposed.
/// 4
public event EventHandler ResponseFetched
{
add
{
if (_ResponseFetched == null)
{
if (!s_responseMap.ContainsKey(_privilege))
{
s_responseMap[_privilege] = this;
}
}
_ResponseFetched += value;
}
remove
{
_ResponseFetched -= value;
if (_ResponseFetched == null)
{
if (s_responseMap.ContainsKey(_privilege))
{
s_responseMap.Remove(_privilege);
}
}
}
}
private event EventHandler _ResponseFetched;
internal void FireEvent(CallCause _cause, RequestResult _result)
{
_ResponseFetched?.Invoke(this, new RequestResponseEventArgs { cause = _cause, result = _result, privilege = _privilege });
}
}
}
internal static class PrivacyPrivilegeManagerErrorFactory
{
static internal Exception GetException(int error)
{
Interop.PrivacyPrivilegeManager.ErrorCode errCode = (Interop.PrivacyPrivilegeManager.ErrorCode)error;
switch (errCode)
{
case Interop.PrivacyPrivilegeManager.ErrorCode.InvalidParameter:
return new ArgumentException("Invalid parameter");
case Interop.PrivacyPrivilegeManager.ErrorCode.IoError:
return new System.IO.IOException("I/O Error");
case Interop.PrivacyPrivilegeManager.ErrorCode.OutOfMemory:
return new OutOfMemoryException("Out of memory");
case Interop.PrivacyPrivilegeManager.ErrorCode.Unknown:
default:
return new ArgumentException("Unknown error");
}
}
}
}