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