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