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 }