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 com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo 32 import kotlinx.coroutines.Job 33 34 /** 35 * A liveData which tracks all packages in the system which have full file permissions, as 36 * represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions. 37 */ 38 object FullStoragePermissionAppsLiveData : 39 SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() { 40 41 private val app: Application = PermissionControllerApplication.get() 42 private val standardPermGroupsPackagesLiveData = 43 PermGroupsPackagesLiveData.get(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) { updateAsync() } <lambda>null54 addSource(AllPackageInfosLiveData) { updateAsync() } 55 } 56 loadDataAndPostValuenull57 override suspend fun loadDataAndPostValue(job: Job) { 58 val storagePackages = standardPermGroupsPackagesLiveData.value?.get(STORAGE) ?: return 59 val appOpsManager = app.getSystemService(AppOpsManager::class.java) ?: return 60 61 val fullStoragePackages = mutableListOf<FullStoragePackageState>() 62 for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) { 63 val userPackages = 64 packageInfoList.filter { 65 storagePackages.contains(it.packageName to user) || 66 it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE) 67 } 68 69 for (packageInfo in userPackages) { 70 fullStoragePackages.add( 71 getFullStorageStateForPackage(appOpsManager, packageInfo, user) ?: continue 72 ) 73 } 74 } 75 76 postValue(fullStoragePackages) 77 } 78 79 /** 80 * Gets the full storage package information for a given package 81 * 82 * @param appOpsManager The App Ops manager to use, if applicable 83 * @param packageInfo The package whose state is to be determined 84 * @param userHandle A preexisting UserHandle object to use. Otherwise, one will be created 85 * @return the FullStoragePackageState for the package, or null if the package does not request 86 * full storage permissions 87 */ getFullStorageStateForPackagenull88 fun getFullStorageStateForPackage( 89 appOpsManager: AppOpsManager, 90 packageInfo: LightPackageInfo, 91 userHandle: UserHandle? = null 92 ): FullStoragePackageState? { 93 val sdk = packageInfo.targetSdkVersion 94 val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid) 95 if (sdk < Build.VERSION_CODES.P) { 96 return FullStoragePackageState( 97 packageInfo.packageName, 98 user, 99 isLegacy = true, 100 isGranted = true 101 ) 102 } else if ( 103 sdk <= Build.VERSION_CODES.Q && 104 appOpsManager.unsafeCheckOpNoThrow( 105 OPSTR_LEGACY_STORAGE, 106 packageInfo.uid, 107 packageInfo.packageName 108 ) == MODE_ALLOWED 109 ) { 110 return FullStoragePackageState( 111 packageInfo.packageName, 112 user, 113 isLegacy = true, 114 isGranted = true 115 ) 116 } 117 if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) { 118 val mode = 119 appOpsManager.unsafeCheckOpNoThrow( 120 OPSTR_MANAGE_EXTERNAL_STORAGE, 121 packageInfo.uid, 122 packageInfo.packageName 123 ) 124 val granted = 125 mode == MODE_ALLOWED || 126 mode == MODE_FOREGROUND || 127 (mode == MODE_DEFAULT && 128 MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions) 129 return FullStoragePackageState( 130 packageInfo.packageName, 131 user, 132 isLegacy = false, 133 isGranted = granted 134 ) 135 } 136 return null 137 } 138 139 /** Recalculate the LiveData TODO ntmyren: Make livedata properly observe app ops */ recalculatenull140 fun recalculate() { 141 updateAsync() 142 } 143 } 144