1 /*
2  * Copyright (C) 2019 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
18 
19 import android.Manifest.permission.MANAGE_EXTERNAL_STORAGE
20 import android.Manifest.permission_group.STORAGE
21 import android.app.AppOpsManager
22 import android.app.AppOpsManager.MODE_ALLOWED
23 import android.app.AppOpsManager.MODE_DEFAULT
24 import android.app.AppOpsManager.MODE_FOREGROUND
25 import android.app.AppOpsManager.OPSTR_LEGACY_STORAGE
26 import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE
27 import android.app.Application
28 import android.os.Build
29 import android.os.UserHandle
30 import com.android.permissioncontroller.PermissionControllerApplication
31 import kotlinx.coroutines.Job
32 
33 /**
34  * A liveData which tracks all packages in the system which have full file permissions, as
35  * represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions.
36  *
37  */
38 object FullStoragePermissionAppsLiveData :
39     SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() {
40 
41     private val app: Application = PermissionControllerApplication.get()
42     private val standardPermGroupsPackagesLiveData = PermGroupsPackagesLiveData.get(
43         customGroups = false)
44 
45     data class FullStoragePackageState(
46         val packageName: String,
47         val user: UserHandle,
48         val isLegacy: Boolean,
49         val isGranted: Boolean
50     )
51 
52     init {
<lambda>null53         addSource(standardPermGroupsPackagesLiveData) {
54             updateAsync()
55         }
<lambda>null56         addSource(AllPackageInfosLiveData) {
57             updateAsync()
58         }
59     }
60 
loadDataAndPostValuenull61     override suspend fun loadDataAndPostValue(job: Job) {
62         val storagePackages = standardPermGroupsPackagesLiveData.value?.get(STORAGE) ?: return
63         val appOpsManager = app.getSystemService(AppOpsManager::class.java) ?: return
64 
65         val fullStoragePackages = mutableListOf<FullStoragePackageState>()
66         for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) {
67             val userPackages = packageInfoList.filter {
68                 storagePackages.contains(it.packageName to user) ||
69                     it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE)
70             }
71 
72             for (packageInfo in userPackages) {
73                 val sdk = packageInfo.targetSdkVersion
74                 if (sdk < Build.VERSION_CODES.P) {
75                     fullStoragePackages.add(FullStoragePackageState(packageInfo.packageName, user,
76                         isLegacy = true, isGranted = true))
77                     continue
78                 } else if (sdk <= Build.VERSION_CODES.Q &&
79                     appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, packageInfo.uid,
80                         packageInfo.packageName) == MODE_ALLOWED) {
81                     fullStoragePackages.add(FullStoragePackageState(packageInfo.packageName, user,
82                         isLegacy = true, isGranted = true))
83                     continue
84                 }
85                 if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) {
86                     val mode = appOpsManager.unsafeCheckOpNoThrow(OPSTR_MANAGE_EXTERNAL_STORAGE,
87                         packageInfo.uid, packageInfo.packageName)
88                     val granted = mode == MODE_ALLOWED || mode == MODE_FOREGROUND ||
89                         (mode == MODE_DEFAULT &&
90                             MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions)
91                     fullStoragePackages.add(FullStoragePackageState(packageInfo.packageName, user,
92                         isLegacy = false, isGranted = granted))
93                 }
94             }
95         }
96 
97         postValue(fullStoragePackages)
98     }
99 
onActivenull100     override fun onActive() {
101         super.onActive()
102         updateAsync()
103     }
104 
105     /**
106      * Recalculate the LiveData
107      * TODO ntmyren: Make livedata properly observe app ops
108      */
recalculatenull109     fun recalculate() {
110         updateAsync()
111     }
112 }