1 /*
<lambda>null2  * Copyright (C) 2022 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.v31
18 
19 import android.app.AppOpsManager
20 import android.app.AppOpsManager.HISTORY_FLAG_DISCRETE
21 import android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
22 import android.app.AppOpsManager.HistoricalOps
23 import android.app.AppOpsManager.HistoricalOpsRequest
24 import android.app.AppOpsManager.OP_FLAG_SELF
25 import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED
26 import android.app.Application
27 import android.os.UserHandle
28 import android.os.UserManager
29 import com.android.modules.utils.build.SdkLevel
30 import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
31 import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightHistoricalPackageOps
32 import java.util.concurrent.TimeUnit
33 import kotlin.coroutines.suspendCoroutine
34 import kotlinx.coroutines.Job
35 
36 /**
37  * LiveData class tracking [LightHistoricalPackageOps] for all packages on the device and for the
38  * provided app ops.
39  *
40  * App ops data is retrieved from [AppOpsManager] and is updated whenever app ops data changes are
41  * heard.
42  */
43 class AllLightHistoricalPackageOpsLiveData(app: Application, val opNames: Set<String>) :
44     SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, LightHistoricalPackageOps>>(),
45     AppOpsManager.OnOpActiveChangedListener,
46     AppOpsManager.OnOpNotedListener,
47     AppOpsManager.OnOpChangedListener {
48 
49     private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
50     private val userManager = app.getSystemService(UserManager::class.java)!!
51 
52     override fun onActive() {
53         super.onActive()
54 
55         opNames.forEach { opName ->
56             // TODO(b/262035952): We watch each active op individually as startWatchingActive only
57             // registers the callback if all ops are valid. Fix this behavior so if one op is
58             // invalid it doesn't affect the other ops.
59             try {
60                 appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, this)
61             } catch (ignored: IllegalArgumentException) {
62                 // Older builds may not support all requested app ops.
63             }
64 
65             try {
66                 appOpsManager.startWatchingMode(opName, /* all packages */ null, this)
67             } catch (ignored: IllegalArgumentException) {
68                 // Older builds may not support all requested app ops.
69             }
70 
71             if (SdkLevel.isAtLeastU()) {
72                 try {
73                     appOpsManager.startWatchingNoted(arrayOf(opName), this)
74                 } catch (ignored: IllegalArgumentException) {
75                     // Older builds may not support all requested app ops.
76                 }
77             }
78         }
79     }
80 
81     override fun onInactive() {
82         super.onInactive()
83 
84         appOpsManager.stopWatchingActive(this)
85         appOpsManager.stopWatchingMode(this)
86     }
87 
88     override suspend fun loadDataAndPostValue(job: Job) {
89         if (job.isCancelled) {
90             return
91         }
92 
93         val allLightHistoricalPackageOps =
94             mutableMapOf<Pair<String, UserHandle>, LightHistoricalPackageOps>()
95 
96         val endTimeMillis = System.currentTimeMillis()
97         val beginTimeMillis = endTimeMillis - TimeUnit.DAYS.toMillis(7)
98 
99         val allProfilesInCurrentUser = userManager.userProfiles
100 
101         val request =
102             HistoricalOpsRequest.Builder(beginTimeMillis, endTimeMillis)
103                 .setFlags(OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED)
104                 .setHistoryFlags(HISTORY_FLAG_DISCRETE or HISTORY_FLAG_GET_ATTRIBUTION_CHAINS)
105                 .build()
106 
107         val historicalOps = suspendCoroutine {
108             appOpsManager.getHistoricalOps(request, { it.run() }) { ops: HistoricalOps ->
109                 it.resumeWith(Result.success(ops))
110             }
111         }
112 
113         for (i in 0 until historicalOps.uidCount) {
114             val historicalUidOps = historicalOps.getUidOpsAt(i)
115             val userHandle = UserHandle.getUserHandleForUid(historicalUidOps.uid)
116             if (userHandle !in allProfilesInCurrentUser) {
117                 continue
118             }
119             for (j in 0 until historicalUidOps.packageCount) {
120                 val historicalPackageOps = historicalUidOps.getPackageOpsAt(j)
121                 allLightHistoricalPackageOps[Pair(historicalPackageOps.packageName, userHandle)] =
122                     LightHistoricalPackageOps(historicalPackageOps, userHandle, opNames)
123             }
124         }
125 
126         postValue(allLightHistoricalPackageOps)
127     }
128 
129     override fun onOpChanged(op: String?, packageName: String?) {
130         update()
131     }
132 
133     override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) {
134         update()
135     }
136 
137     override fun onOpNoted(
138         code: String,
139         uid: Int,
140         packageName: String,
141         attributionTag: String?,
142         flags: Int,
143         result: Int
144     ) {
145         update()
146     }
147 }
148