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