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.app.Application
20 import android.content.pm.PackageManager
21 import android.os.UserHandle
22 import android.util.Log
23 import androidx.annotation.MainThread
24 import androidx.lifecycle.Observer
25 import com.android.permissioncontroller.PermissionControllerApplication
26 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
27 import com.android.permissioncontroller.permission.utils.Utils
28 import kotlinx.coroutines.Job
29 
30 /**
31  * LiveData for a LightPackageInfo.
32  *
33  * @param app The current Application
34  * @param packageName The name of the package this LiveData will watch for mode changes for
35  * @param user The user for whom the packageInfo will be defined
36  */
37 class LightPackageInfoLiveData private constructor(
38     private val app: Application,
39     private val packageName: String,
40     private val user: UserHandle
41 ) : SmartAsyncMediatorLiveData<LightPackageInfo>(alwaysUpdateOnActive = false),
42     PackageBroadcastReceiver.PackageBroadcastListener,
43     PermissionListenerMultiplexer.PermissionChangeCallback {
44 
45     private val LOG_TAG = LightPackageInfoLiveData::class.java.simpleName
46     private val userPackagesLiveData = UserPackageInfosLiveData[user]
47 
48     private var context = Utils.getUserContext(app, user)
49     private var uid: Int? = null
50     /**
51      * The currently registered UID on which this LiveData is listening for permission changes.
52      */
53     private var registeredUid: Int? = null
54     /**
55      * Whether or not this package livedata is watching the UserPackageInfosLiveData
56      */
57     private var watchingUserPackagesLiveData: Boolean = false
58 
59     /**
60      * Callback from the PackageBroadcastReceiver. Either deletes or generates package data.
61      *
62      * @param packageName the name of the package which was updated. Ignored in this method
63      */
64     override fun onPackageUpdate(packageName: String) {
65         updateAsync()
66     }
67 
68     override fun setValue(newValue: LightPackageInfo?) {
69         newValue?.let { packageInfo ->
70             if (packageInfo.uid != uid) {
71                 uid = packageInfo.uid
72 
73                 if (hasActiveObservers()) {
74                     PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid,
75                             packageInfo.uid, this)
76                     registeredUid = uid
77                 }
78             }
79         }
80         super.setValue(newValue)
81     }
82 
83     override fun updateAsync() {
84         // If we were watching the userPackageInfosLiveData, stop, since we will override its value
85         if (watchingUserPackagesLiveData) {
86             removeSource(userPackagesLiveData)
87             watchingUserPackagesLiveData = false
88         }
89         super.updateAsync()
90     }
91 
92     override suspend fun loadDataAndPostValue(job: Job) {
93         if (job.isCancelled) {
94             return
95         }
96         postValue(try {
97             LightPackageInfo(context.packageManager.getPackageInfo(packageName,
98                 PackageManager.GET_PERMISSIONS))
99         } catch (e: PackageManager.NameNotFoundException) {
100             Log.w(LOG_TAG, "Package \"$packageName\" not found")
101             invalidateSingle(packageName to user)
102             null
103         })
104     }
105 
106     /**
107      * Callback from the PermissionListener. Either deletes or generates package data.
108      */
109     override fun onPermissionChange() {
110         updateAsync()
111     }
112 
113     override fun onActive() {
114         super.onActive()
115 
116         PackageBroadcastReceiver.addChangeCallback(packageName, this)
117         uid?.let {
118             registeredUid = uid
119             PermissionListenerMultiplexer.addCallback(it, this)
120         }
121         if (userPackagesLiveData.hasActiveObservers() && !watchingUserPackagesLiveData &&
122             !userPackagesLiveData.permChangeStale) {
123             watchingUserPackagesLiveData = true
124             addSource(userPackagesLiveData, userPackageInfosObserver)
125         } else {
126             updateAsync()
127         }
128     }
129 
130     val userPackageInfosObserver = Observer<List<LightPackageInfo>> {
131         updateFromUserPackageInfosLiveData()
132     }
133 
134     @MainThread
135     private fun updateFromUserPackageInfosLiveData() {
136         if (!userPackagesLiveData.isInitialized) {
137             return
138         }
139 
140         val packageInfo = userPackagesLiveData.value!!.find { it.packageName == packageName }
141         if (packageInfo != null) {
142             // Once we get one non-stale update, stop listening, as any further updates will likely
143             // be individual package updates.
144             if (!userPackagesLiveData.isStale) {
145                 removeSource(UserPackageInfosLiveData[user])
146                 watchingUserPackagesLiveData = false
147             }
148 
149             value = packageInfo
150         } else {
151             // If the UserPackageInfosLiveData does not contain this package, check for removal, and
152             // stop watching.
153             updateAsync()
154         }
155     }
156 
157     override fun onInactive() {
158         super.onInactive()
159 
160         PackageBroadcastReceiver.removeChangeCallback(packageName, this)
161         registeredUid?.let {
162             PermissionListenerMultiplexer.removeCallback(it, this)
163             registeredUid = null
164         }
165         if (watchingUserPackagesLiveData) {
166             removeSource(userPackagesLiveData)
167             watchingUserPackagesLiveData = false
168         }
169     }
170 
171     /**
172      * Repository for LightPackageInfoLiveDatas
173      * <p> Key value is a string package name and UserHandle pair, value is its corresponding
174      * LiveData.
175      */
176     companion object : DataRepositoryForPackage<Pair<String, UserHandle>,
177         LightPackageInfoLiveData>() {
178         override fun newValue(key: Pair<String, UserHandle>): LightPackageInfoLiveData {
179             return LightPackageInfoLiveData(PermissionControllerApplication.get(),
180                 key.first, key.second)
181         }
182     }
183 }
184