1 /* <lambda>null2 * Copyright (C) 2020 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.app.AppOpsManager 20 import android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED 21 import android.app.Application 22 import android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT 23 import android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE 24 import android.os.Handler 25 import android.os.UserHandle 26 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_ELIGIBLE 27 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM 28 import android.permission.PermissionControllerManager.HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER 29 import android.util.Log 30 import com.android.permissioncontroller.PermissionControllerApplication 31 import com.android.permissioncontroller.hibernation.ExemptServicesLiveData 32 import com.android.permissioncontroller.hibernation.HibernationEnabledLiveData 33 import com.android.permissioncontroller.hibernation.isPackageHibernationExemptBySystem 34 import com.android.permissioncontroller.hibernation.isPackageHibernationExemptByUser 35 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS 36 import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState 37 import com.android.permissioncontroller.permission.service.AUTO_REVOKE_EXEMPT_PERMISSIONS 38 import kotlinx.coroutines.Job 39 40 /** 41 * A LiveData which tracks the hibernation/auto-revoke state for one user package. 42 * 43 * @param app The current application 44 * @param packageName The package name whose state we want 45 * @param user The user for whom we want the package 46 */ 47 class HibernationSettingStateLiveData 48 private constructor( 49 private val app: Application, 50 private val packageName: String, 51 private val user: UserHandle 52 ) : SmartAsyncMediatorLiveData<HibernationSettingState>(), AppOpsManager.OnOpChangedListener { 53 54 private val packagePermsLiveData = PackagePermissionsLiveData[packageName, user] 55 private val packageLiveData = LightPackageInfoLiveData[packageName, user] 56 private val permStateLiveDatas = mutableMapOf<String, PermStateLiveData>() 57 private val exemptServicesLiveData = ExemptServicesLiveData[user] 58 private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!! 59 60 // TODO 206455664: remove these once issue is identified 61 private val LOG_TAG = "HibernationSettingStateLiveData" 62 private val DELAY_MS = 3000L 63 private var gotPermLiveDatas: Boolean = false 64 private var gotPastIsUserExempt: Boolean = false 65 private var gotPastIsSystemExempt: Boolean = false 66 67 init { 68 addSource(packagePermsLiveData) { update() } 69 addSource(packageLiveData) { update() } 70 addSource(exemptServicesLiveData) { update() } 71 addSource(HibernationEnabledLiveData) { update() } 72 Handler(app.mainLooper).postDelayed({ logState() }, DELAY_MS) 73 } 74 75 override suspend fun loadDataAndPostValue(job: Job) { 76 if ( 77 !packageLiveData.isInitialized || 78 !packagePermsLiveData.isInitialized || 79 !exemptServicesLiveData.isInitialized 80 ) { 81 return 82 } 83 84 val groups = packagePermsLiveData.value?.keys?.filter { it != NON_RUNTIME_NORMAL_PERMS } 85 val packageInfo = packageLiveData.value 86 if (packageInfo == null || groups == null) { 87 postValue(null) 88 return 89 } 90 val getLiveData = { groupName: String -> PermStateLiveData[packageName, groupName, user] } 91 setSourcesToDifference(groups, permStateLiveDatas, getLiveData) 92 gotPermLiveDatas = true 93 94 if (!permStateLiveDatas.all { it.value.isInitialized }) { 95 return 96 } 97 98 val exemptBySystem = isPackageHibernationExemptBySystem(packageInfo, user) 99 val exemptByUser = isPackageHibernationExemptByUser(app, packageInfo) 100 val eligibility = 101 when { 102 !exemptBySystem && !exemptByUser -> HIBERNATION_ELIGIBILITY_ELIGIBLE 103 exemptBySystem -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM 104 else -> HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER 105 } 106 gotPastIsUserExempt = true 107 val revocableGroups = mutableListOf<String>() 108 if (!isPackageHibernationExemptBySystem(packageInfo, user)) { 109 gotPastIsSystemExempt = true 110 permStateLiveDatas.forEach { (groupName, liveData) -> 111 val default = 112 liveData.value?.any { (_, permState) -> 113 permState.permFlags and 114 (FLAG_PERMISSION_GRANTED_BY_DEFAULT or 115 FLAG_PERMISSION_GRANTED_BY_ROLE) != 0 116 } 117 ?: false 118 val allExempt = 119 liveData.value?.all { (permName, _) -> 120 permName in AUTO_REVOKE_EXEMPT_PERMISSIONS 121 } 122 ?: false 123 if (!default && !allExempt) { 124 revocableGroups.add(groupName) 125 } 126 } 127 } 128 gotPastIsSystemExempt = true 129 130 postValue(HibernationSettingState(eligibility, revocableGroups)) 131 } 132 133 override fun onOpChanged(op: String?, packageName: String?) { 134 if (op == OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED && packageName == packageName) { 135 update() 136 } 137 } 138 139 override fun onActive() { 140 super.onActive() 141 appOpsManager.startWatchingMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageName, this) 142 } 143 144 override fun onInactive() { 145 super.onInactive() 146 appOpsManager.stopWatchingMode(this) 147 } 148 149 // TODO 206455664: remove these once issue is identified 150 private fun logState() { 151 if (!isStale) { 152 return 153 } 154 Log.i( 155 LOG_TAG, 156 "overall state: isStale:$isStale, isInitialized:$isInitialized, " + 157 "value:$value, got perm LiveDatas:$gotPermLiveDatas, " + 158 "got isUserExempt$gotPastIsUserExempt, got isSystemExempt$gotPastIsSystemExempt" 159 ) 160 Log.i( 161 LOG_TAG, 162 "packagePermsLivedata isStale:${packagePermsLiveData.isStale}, " + 163 "isInitialized:${packagePermsLiveData.isInitialized}" 164 ) 165 Log.i( 166 LOG_TAG, 167 "ExemptServicesLiveData isStale:${exemptServicesLiveData.isStale}, " + 168 "isInitialized:${exemptServicesLiveData.isInitialized}" 169 ) 170 Log.i(LOG_TAG, "HibernationEnabledLivedata value:${HibernationEnabledLiveData.value}") 171 for ((group, liveData) in permStateLiveDatas) { 172 Log.i( 173 LOG_TAG, 174 "permStateLivedata $group isStale:${liveData.isStale}, " + 175 "isInitialized:${liveData.isInitialized}" 176 ) 177 } 178 } 179 /** 180 * Repository for HibernationSettingStateLiveDatas. 181 * 182 * <p> Key value is a pair of string package name and UserHandle, value is its corresponding 183 * LiveData. 184 */ 185 companion object : 186 DataRepositoryForPackage<Pair<String, UserHandle>, HibernationSettingStateLiveData>() { 187 override fun newValue(key: Pair<String, UserHandle>): HibernationSettingStateLiveData { 188 return HibernationSettingStateLiveData( 189 PermissionControllerApplication.get(), 190 key.first, 191 key.second 192 ) 193 } 194 } 195 } 196