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