Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
<ItemGroup>
<ProjectReference Include="..\Tizen\Tizen.csproj" />
<ProjectReference Include="..\Tizen.Log\Tizen.Log.csproj" />
<ProjectReference Include="..\Tizen.Applications.Common\Tizen.Applications.Common.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2026 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;

namespace Tizen.Security
{
/// <summary>
/// Exception thrown when one or more required permissions are denied.
/// </summary>
/// <since_tizen> 14 </since_tizen>
/// <example>
/// <code>
/// <![CDATA[
/// try
/// {
/// await PrivacyPrivilegeManager.RequestPermissionsAsync(required: privileges);
/// }
/// catch (PermissionDeniedException ex)
/// {
/// foreach (var privilege in ex.DeniedPrivileges)
/// {
/// var status = ex.GetStatus(privilege);
/// var name = Tizen.Security.Privilege.GetDisplayName("12", privilege);
/// Console.WriteLine($"{name}: {status}");
/// }
/// }
/// ]]>
/// </code>
/// </example>
public class PermissionDeniedException : Exception
{
private readonly IDictionary<string, PermissionStatus> _map;

/// <summary>
/// Gets the list of denied privilege names.
/// </summary>
/// <since_tizen> 14 </since_tizen>
public IEnumerable<string> DeniedPrivileges => _map.Keys;

/// <summary>
/// Initializes a new instance of the PermissionDeniedException class with denied privileges and their statuses.
/// </summary>
/// <param name="denied">A dictionary mapping denied privilege names to their statuses.</param>
/// <since_tizen> 14 </since_tizen>
public PermissionDeniedException(IDictionary<string, PermissionStatus> denied)
: base("Required permissions denied: " + string.Join(", ", denied.Keys))
Comment on lines +61 to +62

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [AI Review]
🟡 Suggestion: denied is dereferenced in the base-constructor call, so a null argument surfaces as NullReferenceException; throw ArgumentNullException explicitly for the new public API.

Suggested change
public PermissionDeniedException(IDictionary<string, PermissionStatus> denied)
: base("Required permissions denied: " + string.Join(", ", denied.Keys))
public PermissionDeniedException(IDictionary<string, PermissionStatus> denied)
: base("Required permissions denied: " + string.Join(", ", (denied ?? throw new ArgumentNullException(nameof(denied))).Keys))

{
_map = new Dictionary<string, PermissionStatus>(denied);
}

/// <summary>
/// Gets the permission status for a specific denied privilege.
/// </summary>
/// <param name="privilege">The privilege name.</param>
/// <returns>The permission status of the denied privilege.</returns>
/// <exception cref="ArgumentException">Thrown when the privilege is not found in the denied list.</exception>
/// <since_tizen> 14 </since_tizen>
public PermissionStatus GetStatus(string privilege)
{
if (!_map.TryGetValue(privilege, out var status))
{
throw new ArgumentException($"Privilege '{privilege}' is not in the denied list.", nameof(privilege));
}
return status;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2026 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;

namespace Tizen.Security
{
/// <summary>
/// Represents the result of permission checks, implementing IDictionary&lt;string, PermissionState&gt;.
/// Provides methods to check if privileges are granted.
/// </summary>
/// <since_tizen>14</since_tizen>
/// <example>
/// <code><![CDATA[
/// PermissionResult permissions = await PrivacyPrivilegeManager.CheckPermissionsAsync(privileges);
///
/// // Check if a specific privilege is granted (returns false for unknown privileges)
/// bool isGranted = permissions.IsGranted("http://tizen.org/privilege/account.read");
///
/// // Check if all privileges are granted
/// bool allGranted = permissions.AllGranted();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [AI Review]
🟡 Suggestion: AllGranted() doesn't exist on this class — the method declared below is AreAllGranted().

Suggested change
/// bool allGranted = permissions.AllGranted();
/// bool allGranted = permissions.AreAllGranted();

/// ]]></code>
/// </example>
public class PermissionResult : Dictionary<string, PermissionStatus>
{
/// <summary>
/// Checks if a specific privilege is granted.
/// Returns false for unknown privileges without throwing an exception.
/// </summary>
/// <param name="privilege">The privilege to check.</param>
/// <returns>True if the privilege is granted; otherwise, false.</returns>
/// <since_tizen>14</since_tizen>
public bool IsGranted(string privilege) =>
TryGetValue(privilege, out var status) && status.IsGranted();

/// <summary>
/// Checks if all privileges in this result are granted.
/// </summary>
/// <returns>True if all privileges are granted; otherwise, false.</returns>
/// <since_tizen>14</since_tizen>
public bool AreAllGranted() => Values.All(status => status.IsGranted());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2026 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.Security
{
/// <summary>
/// Enumeration for the status of a permission.
/// </summary>
/// <since_tizen> 14 </since_tizen>
public enum PermissionStatus
{
/// <summary>
/// The privilege is granted.
/// </summary>
Granted = 0,
/// <summary>
/// The privilege is denied.
/// </summary>
Denied = 1,
/// <summary>
/// The permission is denied until user grants it. Use RequestPermissionAsync() to ask the user for the permission.
/// </summary>
Ask = 2,
/// <summary>
/// The privilege is granted for the current session only.
/// </summary>
GrantedSession = 3,
/// <summary>
/// The privilege is denied for the current session only.
/// </summary>
DeniedSession = 4,
/// <summary>
/// The privilege is granted only while the application is in foreground.
/// </summary>
GrantedInUse = 5,
/// <summary>
/// The privilege is denied once - user did not allow the permission this time and chose to be asked again later.
/// </summary>
Deferred = 90,
}

/// <summary>
/// Extension methods for PermissionState.
/// </summary>
/// <since_tizen> 14 </since_tizen>
public static class PermissionStateExtensions
Comment on lines +55 to +59

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [AI Review]
🟡 Suggestion: This new public class is named after a PermissionState type that doesn't exist — the enum it extends is PermissionStatus, and the name becomes permanent public API once approved.

Suggested change
/// <summary>
/// Extension methods for PermissionState.
/// </summary>
/// <since_tizen> 14 </since_tizen>
public static class PermissionStateExtensions
/// <summary>
/// Extension methods for PermissionStatus.
/// </summary>
/// <since_tizen> 14 </since_tizen>
public static class PermissionStatusExtensions

{
/// <summary>
/// Checks if the permission state indicates that the privilege is granted.
/// </summary>
/// <param name="state">The permission state to check.</param>
/// <returns>True if the privilege is granted (Granted, GrantedSession, or GrantedInUse); otherwise, false.</returns>
/// <since_tizen> 14 </since_tizen>
public static bool IsGranted(this PermissionStatus state)
{
return state == PermissionStatus.Granted ||
state == PermissionStatus.GrantedSession ||
state == PermissionStatus.GrantedInUse;
}
}

/// <summary>
/// Internal helper class for converting between interop enums and PermissionStatus.
/// </summary>
internal static class PermissionStatusConverter
{
/// <summary>
/// Converts Interop.CheckResult to PermissionState.
/// </summary>
internal static PermissionStatus ToPermissionStatus(this Interop.PrivacyPrivilegeManager.CheckResult checkResult)
{
// Values match directly for all cases.
return (PermissionStatus)checkResult;
}

/// <summary>
/// Converts RequestResult to PermissionState.
/// </summary>
internal static PermissionStatus ToPermissionStatus(this RequestResult requestResult)
{
// Value DenyOnce maps to Deferred (90). Others match directly.
if (requestResult == RequestResult.DenyOnce)
{
return PermissionStatus.Deferred;
}
return (PermissionStatus)requestResult;
}
}
}
Loading
Loading