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 */ 39 object FullStoragePermissionAppsLiveData : 40 SmartAsyncMediatorLiveData<List<FullStoragePermissionAppsLiveData.FullStoragePackageState>>() { 41 42 private val app: Application = PermissionControllerApplication.get() 43 private val standardPermGroupsPackagesLiveData = PermGroupsPackagesLiveData.get( 44 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) { 55 updateAsync() 56 } <lambda>null57 addSource(AllPackageInfosLiveData) { 58 updateAsync() 59 } 60 } 61 loadDataAndPostValuenull62 override suspend fun loadDataAndPostValue(job: Job) { 63 val storagePackages = standardPermGroupsPackagesLiveData.value?.get(STORAGE) ?: return 64 val appOpsManager = app.getSystemService(AppOpsManager::class.java) ?: return 65 66 val fullStoragePackages = mutableListOf<FullStoragePackageState>() 67 for ((user, packageInfoList) in AllPackageInfosLiveData.value ?: emptyMap()) { 68 val userPackages = packageInfoList.filter { 69 storagePackages.contains(it.packageName to user) || 70 it.requestedPermissions.contains(MANAGE_EXTERNAL_STORAGE) 71 } 72 73 for (packageInfo in userPackages) { 74 fullStoragePackages.add(getFullStorageStateForPackage(appOpsManager, 75 packageInfo, user) ?: continue) 76 } 77 } 78 79 postValue(fullStoragePackages) 80 } 81 82 /** 83 * Gets the full storage package information for a given package 84 * 85 * @param appOpsManager The App Ops manager to use, if applicable 86 * @param packageInfo The package whose state is to be determined 87 * @param userHandle A preexisting UserHandle object to use. Otherwise, one will be created 88 * 89 * @return the FullStoragePackageState for the package, or null if the package does not request 90 * full storage permissions 91 */ getFullStorageStateForPackagenull92 fun getFullStorageStateForPackage( 93 appOpsManager: AppOpsManager, 94 packageInfo: LightPackageInfo, 95 userHandle: UserHandle? = null 96 ): FullStoragePackageState? { 97 val sdk = packageInfo.targetSdkVersion 98 val user = userHandle ?: UserHandle.getUserHandleForUid(packageInfo.uid) 99 if (sdk < Build.VERSION_CODES.P) { 100 return FullStoragePackageState(packageInfo.packageName, user, 101 isLegacy = true, isGranted = true) 102 } else if (sdk <= Build.VERSION_CODES.Q && 103 appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, packageInfo.uid, 104 packageInfo.packageName) == MODE_ALLOWED) { 105 return FullStoragePackageState(packageInfo.packageName, user, 106 isLegacy = true, isGranted = true) 107 } 108 if (MANAGE_EXTERNAL_STORAGE in packageInfo.requestedPermissions) { 109 val mode = appOpsManager.unsafeCheckOpNoThrow(OPSTR_MANAGE_EXTERNAL_STORAGE, 110 packageInfo.uid, packageInfo.packageName) 111 val granted = mode == MODE_ALLOWED || mode == MODE_FOREGROUND || 112 (mode == MODE_DEFAULT && 113 MANAGE_EXTERNAL_STORAGE in packageInfo.grantedPermissions) 114 return FullStoragePackageState(packageInfo.packageName, user, 115 isLegacy = false, isGranted = granted) 116 } 117 return null 118 } 119 120 /** 121 * Recalculate the LiveData 122 * TODO ntmyren: Make livedata properly observe app ops 123 */ recalculatenull124 fun recalculate() { 125 updateAsync() 126 } 127 }