1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.permissioncontroller.permission.data.repository.v31
18 
19 import android.app.Application
20 import android.content.Context
21 import android.content.pm.PackageItemInfo
22 import android.content.pm.PackageManager
23 import android.os.UserHandle
24 import android.text.TextUtils
25 import com.android.modules.utils.build.SdkLevel
26 import com.android.permissioncontroller.permission.utils.PermissionMapping
27 import kotlin.concurrent.Volatile
28 import kotlinx.coroutines.CoroutineDispatcher
29 import kotlinx.coroutines.Dispatchers
30 import kotlinx.coroutines.withContext
31 
32 /**
33  * This repository encapsulates permission data (i.e. permission or permission group definitions,
34  * package permission states etc.) exposed by [android.permission.PermissionManager] and
35  * [PackageManager].
36  */
37 interface PermissionRepository {
38     /**
39      * Gets the flags associated with a permission.
40      *
41      * @see PackageManager.getPermissionFlags
42      */
getPermissionFlagsnull43     suspend fun getPermissionFlags(
44         permissionName: String,
45         packageName: String,
46         user: UserHandle
47     ): Int
48 
49     /**
50      * Gets a permission group label for a given permission group.
51      *
52      * @see PackageManager.getPermissionGroupInfo
53      * @see PackageItemInfo.loadSafeLabel
54      */
55     suspend fun getPermissionGroupLabel(context: Context, groupName: String): CharSequence
56 
57     /** Gets a list of permission group to be shown in the privacy dashboard. */
58     fun getPermissionGroupsForPrivacyDashboard(): List<String>
59 
60     companion object {
61         @Volatile private var instance: PermissionRepository? = null
62 
63         fun getInstance(application: Application): PermissionRepository =
64             instance
65                 ?: synchronized(this) {
66                     PermissionRepositoryImpl(application).also { instance = it }
67                 }
68     }
69 }
70 
71 class PermissionRepositoryImpl(
72     application: Application,
73     private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
74 ) : PermissionRepository {
75     private val packageManager = application.packageManager
76 
getPermissionFlagsnull77     override suspend fun getPermissionFlags(
78         permissionName: String,
79         packageName: String,
80         user: UserHandle
81     ): Int =
82         withContext(dispatcher) {
83             packageManager.getPermissionFlags(permissionName, packageName, user)
84         }
85 
86     /**
87      * Gets a permission group's label from the system.
88      *
89      * @param context The context from which to get the label
90      * @param groupName The name of the permission group whose label we want
91      * @return The permission group's label, or the group name, if the group is invalid
92      */
getPermissionGroupLabelnull93     override suspend fun getPermissionGroupLabel(
94         context: Context,
95         groupName: String
96     ): CharSequence =
97         withContext(dispatcher) {
98             val groupInfo = getPermissionGroupInfo(groupName, context)
99             groupInfo?.loadSafeLabel(
100                 context.packageManager,
101                 0f,
102                 TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
103             )
104                 ?: groupName
105         }
106 
107     /**
108      * Get the [PackageItemInfo] for the given permission group.
109      *
110      * @param groupName the group
111      * @param context the `Context` to retrieve `PackageManager`
112      * @return The info of permission group or null if the group does not have runtime permissions.
113      */
getPermissionGroupInfonull114     private fun getPermissionGroupInfo(groupName: String, context: Context): PackageItemInfo? {
115         return try {
116             context.packageManager.getPermissionGroupInfo(groupName, 0)
117         } catch (e: PackageManager.NameNotFoundException) {
118             null
119         }
120             ?: try {
121                 context.packageManager.getPermissionInfo(groupName, 0)
122             } catch (e: PackageManager.NameNotFoundException) {
123                 null
124             }
125     }
126 
getPermissionGroupsForPrivacyDashboardnull127     override fun getPermissionGroupsForPrivacyDashboard(): List<String> {
128         return if (SdkLevel.isAtLeastT()) {
129             PermissionMapping.getPlatformPermissionGroups().filter {
130                 it != android.Manifest.permission_group.NOTIFICATIONS
131             }
132         } else {
133             PermissionMapping.getPlatformPermissionGroups()
134         }
135     }
136 }
137