1 /*
<lambda>null2  * Copyright (C) 2021 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.os.UserHandle
20 import android.provider.DeviceConfig
21 import android.provider.DeviceConfig.NAMESPACE_PERMISSIONS
22 import android.util.ArraySet
23 import android.util.Log
24 import com.android.permissioncontroller.PermissionControllerApplication
25 import com.android.permissioncontroller.hibernation.getUnusedThresholdMs
26 import com.android.permissioncontroller.hibernation.isHibernationEnabled
27 import com.android.permissioncontroller.hibernation.lastTimePackageUsed
28 import com.android.permissioncontroller.permission.utils.Utils
29 
30 /**
31  * Gets all unused packages from an existing live data that have not been opened in a few months and
32  * the permission groups that have been revoked for them, if any. This will let us removed used apps
33  * from the Unused Apps screen.
34  *
35  * @param sourceLiveData the live data for packages to base this list of unused apps on
36  *
37  * ```(packageName, user) -> [groupName]```
38  */
39 class UnusedPackagesLiveData(
40     private val sourceLiveData: SmartUpdateMediatorLiveData<Set<Pair<String, UserHandle>>>
41 ) : SmartUpdateMediatorLiveData<Map<Pair<String, UserHandle>, Set<String>>>() {
42 
43     private val LOG_TAG = UnusedPackagesLiveData::class.java.simpleName
44 
45     private var unusedThreshold = getUnusedThresholdMs()
46     private var usageStatsLiveData = UsageStatsLiveData[unusedThreshold]
47 
48     init {
49         addSource(usageStatsLiveData) { update() }
50         addSource(AutoRevokedPackagesLiveData) { update() }
51         addSource(sourceLiveData) { update() }
52         DeviceConfig.addOnPropertiesChangedListener(
53             NAMESPACE_PERMISSIONS,
54             PermissionControllerApplication.get().mainExecutor,
55             { properties ->
56                 for (key in properties.keyset) {
57                     if (key == Utils.PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS) {
58                         removeSource(usageStatsLiveData)
59                         unusedThreshold = getUnusedThresholdMs()
60                         usageStatsLiveData = UsageStatsLiveData[unusedThreshold]
61                         addSource(usageStatsLiveData) { update() }
62                     }
63                 }
64             }
65         )
66     }
67 
68     override fun onUpdate() {
69         if (
70             !usageStatsLiveData.isInitialized ||
71                 !AutoRevokedPackagesLiveData.isInitialized ||
72                 !sourceLiveData.isInitialized
73         ) {
74             return
75         }
76 
77         val sourcePackages = sourceLiveData.value!!
78         val autoRevokedPackages = AutoRevokedPackagesLiveData.value!!
79 
80         val unusedPackages = mutableMapOf<Pair<String, UserHandle>, Set<String>>()
81         for (userPackage in sourcePackages) {
82             val perms = autoRevokedPackages[userPackage] ?: ArraySet()
83             unusedPackages[userPackage] = perms.toSet()
84         }
85 
86         val now = System.currentTimeMillis()
87         for ((user, stats) in usageStatsLiveData.value!!) {
88             for (stat in stats) {
89                 val userPackage = stat.packageName to user
90                 if (
91                     userPackage in autoRevokedPackages &&
92                         (now - stat.lastTimePackageUsed()) < unusedThreshold
93                 ) {
94                     unusedPackages.remove(userPackage)
95                 }
96             }
97         }
98 
99         Log.i(LOG_TAG, "onUpdate() -> $unusedPackages")
100 
101         value = unusedPackages
102     }
103 }
104 
getUnusedPackagesnull105 fun getUnusedPackages(): UnusedPackagesLiveData {
106     return if (isHibernationEnabled()) {
107         unusedHibernatedOrRevokedPackagesLiveData
108     } else {
109         unusedAutoRevokePackagesLiveData
110     }
111 }
112