1 /* 2 * 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.Application 20 import android.content.pm.PackageManager 21 import android.util.Log 22 import com.android.permissioncontroller.PermissionControllerApplication 23 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo 24 import com.android.permissioncontroller.permission.utils.Utils.OS_PKG 25 import com.android.permissioncontroller.permission.utils.Utils.isRuntimePlatformPermission 26 import kotlinx.coroutines.Job 27 28 /** 29 * LiveData for a LightPermInfo. 30 * 31 * @param app current Application 32 * @param permissionName name of the permission this LiveData will watch for mode changes for 33 */ 34 class LightPermInfoLiveData private constructor( 35 private val app: Application, 36 private val permissionName: String 37 ) : SmartAsyncMediatorLiveData<LightPermInfo>(), 38 PackageBroadcastReceiver.PackageBroadcastListener { 39 40 private val LOG_TAG = LightPermInfoLiveData::class.java.simpleName 41 42 /** Is this liveData currently listing for changes */ 43 private var isListeningForChanges = false 44 45 /** 46 * Callback from the PackageBroadcastReceiver. 47 * 48 * <p>Package updates might change permission properties 49 */ 50 override fun onPackageUpdate(ignored: String) { 51 updateAsync() 52 } 53 54 override fun updateAsync() { 55 // No need to update if the value can never change 56 if (value != null && isImmutable()) { 57 return 58 } 59 60 super.updateAsync() 61 } 62 63 override suspend fun loadDataAndPostValue(job: Job) { 64 if (job.isCancelled) { 65 return 66 } 67 68 val newValue = try { 69 LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0)) 70 } catch (e: PackageManager.NameNotFoundException) { 71 Log.w(LOG_TAG, "Permission \"$permissionName\" not found") 72 invalidateSingle(permissionName) 73 null 74 } 75 76 if (isImmutable()) { 77 stopListeningForChanges() 78 } 79 80 postValue(newValue) 81 } 82 83 /** 84 * @return if the permission state can never change 85 */ 86 private fun isImmutable(): Boolean { 87 // The os package never changes 88 value?.let { 89 if (it.packageName == OS_PKG) { 90 return true 91 } 92 } 93 94 // Platform permissions never change 95 return isRuntimePlatformPermission(permissionName) 96 } 97 98 /** 99 * Start listing for changes to this permission if needed 100 */ 101 private fun startListeningForChanges() { 102 if (!isListeningForChanges && !isImmutable()) { 103 isListeningForChanges = true 104 PackageBroadcastReceiver.addAllCallback(this) 105 } 106 } 107 108 /** 109 * Stop listing for changes to this permission 110 */ 111 private fun stopListeningForChanges() { 112 if (isListeningForChanges) { 113 PackageBroadcastReceiver.removeAllCallback(this) 114 isListeningForChanges = false 115 } 116 } 117 118 override fun onActive() { 119 super.onActive() 120 121 startListeningForChanges() 122 } 123 124 override fun onInactive() { 125 super.onInactive() 126 127 stopListeningForChanges() 128 } 129 130 /** 131 * Repository for LightPermInfoLiveData 132 * 133 * <p>Key value is a string permission name, value is its corresponding LiveData. 134 */ 135 companion object : DataRepositoryForPackage<String, LightPermInfoLiveData>() { 136 override fun newValue(key: String): LightPermInfoLiveData { 137 return LightPermInfoLiveData(PermissionControllerApplication.get(), key) 138 } 139 } 140 } 141