1 /*
<lambda>null2  * Copyright (C) 2018 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 android.app.appops.cts
18 
19 import android.app.AppOpsManager
20 import android.app.AppOpsManager.HistoricalOp
21 import android.app.AppOpsManager.HistoricalOps
22 import android.app.AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES
23 import android.app.AppOpsManager.OP_FLAGS_ALL
24 import android.os.Process
25 import android.os.SystemClock
26 import android.provider.DeviceConfig
27 import androidx.test.InstrumentationRegistry
28 import androidx.test.rule.ActivityTestRule
29 import androidx.test.runner.AndroidJUnit4
30 import androidx.test.uiautomator.UiDevice
31 import com.google.common.truth.Truth.assertThat
32 import org.junit.After
33 import org.junit.Before
34 import org.junit.Rule
35 import org.junit.Test
36 import org.junit.runner.RunWith
37 import java.time.Instant
38 import java.util.concurrent.TimeUnit
39 import java.util.concurrent.locks.ReentrantLock
40 import java.util.function.Consumer
41 
42 const val PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"
43 
44 @RunWith(AndroidJUnit4::class)
45 class HistoricalAppopsTest {
46     private val uid = Process.myUid()
47     private lateinit var appOpsManager: AppOpsManager
48     private lateinit var packageName: String
49 
50     private var wasPermissionsHubEnabled = false
51 
52     // Start an activity to make sure this app counts as being in the foreground
53     @Rule @JvmField
54     var activityRule = ActivityTestRule(UidStateForceActivity::class.java)
55 
56     @Before
57     fun wakeScreenUp() {
58         val uiDevice = UiDevice.getInstance(instrumentation)
59         uiDevice.wakeUp()
60         uiDevice.executeShellCommand("wm dismiss-keyguard")
61     }
62 
63     @Before
64     fun setUpTest() {
65         appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
66         packageName = context.packageName!!
67         runWithShellPermissionIdentity {
68             wasPermissionsHubEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
69                     PROPERTY_PERMISSIONS_HUB_ENABLED, false)
70             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
71                     PROPERTY_PERMISSIONS_HUB_ENABLED, true.toString(), false)
72             appOpsManager.clearHistory()
73             appOpsManager.resetHistoryParameters()
74         }
75     }
76 
77     @After
78     fun tearDownTest() {
79         runWithShellPermissionIdentity {
80             appOpsManager.clearHistory()
81             appOpsManager.resetHistoryParameters()
82             DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
83                     PROPERTY_PERMISSIONS_HUB_ENABLED, wasPermissionsHubEnabled.toString(), false)
84         }
85     }
86 
87     @Test
88     fun testGetHistoricalPackageOpsForegroundAccessInMemoryBucket() {
89         testGetHistoricalPackageOpsForegroundAtDepth(0)
90     }
91 
92     @Test
93     fun testGetHistoricalPackageOpsForegroundAccessFirstOnDiskBucket() {
94         testGetHistoricalPackageOpsForegroundAtDepth(1)
95     }
96 
97     @Test
98     fun testHistoricalAggregationOneLevelsDeep() {
99         testHistoricalAggregationSomeLevelsDeep(0)
100     }
101 
102     @Test
103     fun testHistoricalAggregationTwoLevelsDeep() {
104         testHistoricalAggregationSomeLevelsDeep(1)
105     }
106 
107     @Test
108     fun testRebootHistory() {
109         // Configure historical registry behavior.
110         setHistoryParameters(
111                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
112                 SNAPSHOT_INTERVAL_MILLIS,
113                 INTERVAL_COMPRESSION_MULTIPLIER)
114 
115         // Add the data to the history
116         val chunk = createDataChunk()
117         val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3
118         for (i in 0 until chunkCount) {
119             addHistoricalOps(chunk)
120         }
121 
122         // Validate the data for the first interval
123         val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
124         val firstIntervalEndMillis = computeIntervalBeginRawMillis(1)
125         var firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
126                 firstIntervalBeginMillis, firstIntervalEndMillis)
127         assertHasCounts(firstOps!!, 197)
128 
129         // Validate the data for the second interval
130         val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
131         val secondIntervalEndMillis = computeIntervalBeginRawMillis(2)
132         var secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
133                 secondIntervalBeginMillis, secondIntervalEndMillis)
134         assertHasCounts(secondOps!!, 33)
135 
136         // Validate the data for all intervals
137         val everythingIntervalBeginMillis = Instant.EPOCH.toEpochMilli()
138         val everythingIntervalEndMillis = Long.MAX_VALUE
139         var allOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
140                 everythingIntervalBeginMillis, everythingIntervalEndMillis)
141         assertHasCounts(allOps!!, 230)
142 
143         // Now reboot the history
144         runWithShellPermissionIdentity {
145             appOpsManager.rebootHistory(firstIntervalEndMillis)
146         }
147 
148         // Validate the data for the first interval
149         firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
150                 firstIntervalBeginMillis, firstIntervalEndMillis)
151         assertHasCounts(firstOps!!, 0)
152 
153         // Validate the data for the second interval
154         secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
155                 secondIntervalBeginMillis, secondIntervalEndMillis)
156         assertHasCounts(secondOps!!, 230)
157 
158         // Validate the data for all intervals
159         allOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
160                 everythingIntervalBeginMillis, everythingIntervalEndMillis)
161         assertHasCounts(allOps!!, 230)
162 
163         // Write some more ops to the first interval
164         for (i in 0 until chunkCount) {
165             addHistoricalOps(chunk)
166         }
167 
168         // Validate the data for the first interval
169         firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
170                 firstIntervalBeginMillis, firstIntervalEndMillis)
171         assertHasCounts(firstOps!!, 197)
172 
173         // Validate the data for the second interval
174         secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
175                 secondIntervalBeginMillis, secondIntervalEndMillis)
176         assertHasCounts(secondOps!!, 263)
177 
178         // Validate the data for all intervals
179         allOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
180                 everythingIntervalBeginMillis, everythingIntervalEndMillis)
181         assertHasCounts(allOps!!, 460)
182     }
183 
184     @Test
185     fun testHistoricalAggregationOverflow() {
186         // Configure historical registry behavior.
187         setHistoryParameters(
188                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
189                 SNAPSHOT_INTERVAL_MILLIS,
190                 INTERVAL_COMPRESSION_MULTIPLIER)
191 
192         // Add the data to the history
193         val chunk = createDataChunk()
194         val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3
195         for (i in 0 until chunkCount) {
196             addHistoricalOps(chunk)
197         }
198 
199         // Validate the data for the first interval
200         val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
201         val firstIntervalEndMillis = computeIntervalBeginRawMillis(1)
202         val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
203                 firstIntervalBeginMillis, firstIntervalEndMillis)
204         assertHasCounts(firstOps!!, 197)
205 
206         // Validate the data for the second interval
207         val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
208         val secondIntervalEndMillis = computeIntervalBeginRawMillis(2)
209         val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
210                 secondIntervalBeginMillis, secondIntervalEndMillis)
211         assertHasCounts(secondOps!!, 33)
212 
213         // Validate the data for both intervals
214         val thirdIntervalBeginMillis = firstIntervalEndMillis - SNAPSHOT_INTERVAL_MILLIS
215         val thirdIntervalEndMillis = secondIntervalBeginMillis + SNAPSHOT_INTERVAL_MILLIS
216         val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
217                 thirdIntervalBeginMillis, thirdIntervalEndMillis)
218         assertHasCounts(thirdOps!!, 33)
219     }
220 
221     @Test
222     fun testHistoryTimeTravel() {
223         // Configure historical registry behavior.
224         setHistoryParameters(
225                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
226                 SNAPSHOT_INTERVAL_MILLIS,
227                 INTERVAL_COMPRESSION_MULTIPLIER)
228 
229         // Fill the first two intervals with data
230         val chunk = createDataChunk()
231         val chunkCount = computeSlotCount(2) * SNAPSHOT_INTERVAL_MILLIS / chunk.endTimeMillis
232         for (i in 0 until chunkCount) {
233             addHistoricalOps(chunk)
234         }
235 
236         // Move history in past with the first interval duration
237         val firstIntervalDurationMillis = computeIntervalDurationMillis(0)
238         runWithShellPermissionIdentity {
239             appOpsManager.offsetHistory(firstIntervalDurationMillis)
240         }
241 
242         // Validate the data for the first interval
243         val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
244         val firstIntervalEndMillis = firstIntervalBeginMillis + firstIntervalDurationMillis
245         val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
246                 firstIntervalBeginMillis, firstIntervalEndMillis)
247         assertThat(firstOps).isNotNull()
248         assertThat(firstOps!!.uidCount).isEqualTo(0)
249 
250         // Validate the data for the second interval
251         val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
252         val secondIntervalDurationMillis = computeIntervalDurationMillis(1)
253         val secondIntervalEndMillis = secondIntervalBeginMillis + secondIntervalDurationMillis
254         val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
255                 secondIntervalBeginMillis, secondIntervalEndMillis)
256         val secondChunkCount = ((computeSlotCount(2) - computeSlotCount(1))
257             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
258         assertHasCounts(secondOps!!, 10 * secondChunkCount)
259 
260         // Validate the data for the third interval
261         val thirdIntervalBeginMillis = computeIntervalBeginRawMillis(2)
262         val thirdIntervalDurationMillis = computeIntervalDurationMillis(2)
263         val thirdIntervalEndMillis = thirdIntervalBeginMillis + thirdIntervalDurationMillis
264         val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
265                 thirdIntervalBeginMillis, thirdIntervalEndMillis)
266         val thirdChunkCount = secondChunkCount / INTERVAL_COMPRESSION_MULTIPLIER
267         assertHasCounts(thirdOps!!, 10 * thirdChunkCount)
268 
269         // Move history in future with the first interval duration
270         runWithShellPermissionIdentity {
271             appOpsManager.offsetHistory(-(2.5f * firstIntervalDurationMillis).toLong())
272         }
273 
274         // Validate the data for the first interval
275         val fourthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
276                 firstIntervalBeginMillis, firstIntervalEndMillis)
277         assertHasCounts(fourthOps!!, 194)
278 
279         // Validate the data for the second interval
280         val fifthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
281                 secondIntervalBeginMillis, secondIntervalEndMillis)
282 
283         assertThat(fifthOps).isNotNull()
284         assertHasCounts(fifthOps!!, 1703)
285     }
286 
287     @Test
288     fun testGetHistoricalAggregationOverAttributions() {
289         // Configure historical registry behavior.
290         setHistoryParameters(
291                 AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
292                 SNAPSHOT_INTERVAL_MILLIS,
293                 INTERVAL_COMPRESSION_MULTIPLIER)
294 
295         setUidMode(OPSTR_REQUEST_DELETE_PACKAGES, uid, AppOpsManager.MODE_ALLOWED)
296 
297         UidStateForceActivity.waitForResumed()
298 
299         appOpsManager.noteOp(OPSTR_REQUEST_DELETE_PACKAGES, uid, packageName, "firstAttribution",
300                 null)
301         appOpsManager.noteOp(OPSTR_REQUEST_DELETE_PACKAGES, uid, packageName, "secondAttribution",
302                 null)
303         var memOps: AppOpsManager.HistoricalOps? = null
304         eventually(SNAPSHOT_INTERVAL_MILLIS / 2) {
305             memOps = getHistoricalOps(appOpsManager, uid = uid)!!
306 
307             assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
308                     .getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!.getForegroundAccessCount(OP_FLAGS_ALL))
309                     .isEqualTo(2)
310             assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
311                     .getAttributedOps("firstAttribution")!!.getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!
312                     .getForegroundAccessCount(OP_FLAGS_ALL)).isEqualTo(1)
313             assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
314                     .getAttributedOps("secondAttribution")!!.getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!
315                     .getForegroundAccessCount(OP_FLAGS_ALL)).isEqualTo(1)
316         }
317 
318         // Wait until data is on disk and verify no entry got lost
319         Thread.sleep(SNAPSHOT_INTERVAL_MILLIS)
320 
321         val diskOps = getHistoricalOps(appOpsManager, uid = uid)!!
322         assertThat(diskOps.getUidOpsAt(0)).isEqualTo(memOps?.getUidOpsAt(0))
323     }
324 
325     private fun testHistoricalAggregationSomeLevelsDeep(depth: Int) {
326         // Configure historical registry behavior.
327         setHistoryParameters(
328                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
329                 SNAPSHOT_INTERVAL_MILLIS,
330                 INTERVAL_COMPRESSION_MULTIPLIER)
331 
332         // Add the data to the history
333         val chunk = createDataChunk()
334         val chunkCount = (computeSlotCount(depth + 1)
335             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
336         for (i in 0 until chunkCount) {
337             addHistoricalOps(chunk)
338         }
339 
340         // Validate the data for the full interval
341         val intervalBeginMillis = computeIntervalBeginRawMillis(depth)
342         val intervalEndMillis = computeIntervalBeginRawMillis(depth + 1)
343         val ops = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
344                 intervalBeginMillis, intervalEndMillis)
345         val expectedOpCount = ((computeSlotCount(depth + 1) - computeSlotCount(depth))
346             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) * 10
347         assertHasCounts(ops!!, expectedOpCount)
348     }
349 
350     private fun testGetHistoricalPackageOpsForegroundAtDepth(depth: Int) {
351         // Configure historical registry behavior.
352         setHistoryParameters(
353                 AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
354                 SNAPSHOT_INTERVAL_MILLIS,
355                 INTERVAL_COMPRESSION_MULTIPLIER)
356 
357         setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
358                 AppOpsManager.MODE_ALLOWED)
359         setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
360                 AppOpsManager.MODE_ALLOWED)
361 
362         UidStateForceActivity.waitForResumed()
363 
364         try {
365             val noteCount = 5
366 
367             var beginTimeMillis = 0L
368             var endTimeMillis = 0L
369 
370             // Note ops such that we have data at all levels
371             for (d in depth downTo 0) {
372                 for (i in 0 until noteCount) {
373                     appOpsManager.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName)
374                 }
375 
376                 if (d > 0) {
377                     val previousIntervalDuration = computeIntervalDurationMillis(d - 2)
378                     val currentIntervalDuration = computeIntervalDurationMillis(d - 1)
379 
380                     endTimeMillis -= previousIntervalDuration
381                     beginTimeMillis -= currentIntervalDuration
382 
383                     val sleepDurationMillis = currentIntervalDuration / 2
384                     SystemClock.sleep(sleepDurationMillis)
385                 }
386             }
387 
388             val nowMillis = System.currentTimeMillis()
389             if (depth > 0) {
390                 beginTimeMillis += nowMillis
391                 endTimeMillis += nowMillis
392             } else {
393                 beginTimeMillis = nowMillis - SNAPSHOT_INTERVAL_MILLIS
394                 endTimeMillis = Long.MAX_VALUE
395             }
396 
397             // Get all ops for the package
398             val allOps = getHistoricalOps(appOpsManager, uid, packageName,
399                     null, beginTimeMillis, endTimeMillis)
400 
401             assertThat(allOps).isNotNull()
402             assertThat(allOps!!.uidCount).isEqualTo(1)
403             assertThat(allOps.beginTimeMillis).isEqualTo(beginTimeMillis)
404             assertThat(allOps.endTimeMillis).isGreaterThan(beginTimeMillis)
405 
406             val uidOps = allOps.getUidOpsAt(0)
407             assertThat(uidOps).isNotNull()
408             assertThat(uidOps.uid).isEqualTo(Process.myUid())
409             assertThat(uidOps.packageCount).isEqualTo(1)
410 
411             val packageOps = uidOps.getPackageOpsAt(0)
412             assertThat(packageOps).isNotNull()
413             assertThat(packageOps.packageName).isEqualTo(packageName)
414             assertThat(packageOps.opCount).isEqualTo(1)
415 
416             val op = packageOps.getOpAt(0)
417             assertThat(op).isNotNull()
418             assertThat(op.opName).isEqualTo(AppOpsManager.OPSTR_START_FOREGROUND)
419 
420             assertThat(op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL))
421                     .isEqualTo(noteCount)
422             assertThat(op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
423             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0)
424             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(noteCount)
425             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION))
426                     .isEqualTo(0)
427             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE))
428                     .isEqualTo(0)
429             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0)
430             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
431             assertThat(getAccessCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
432 
433             assertThat(op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
434             assertThat(op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
435             assertThat(op.getAccessDuration(AppOpsManager.UID_STATE_TOP,
436                     AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAGS_ALL))
437                     .isEqualTo(0)
438             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0)
439             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(0)
440             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION))
441                     .isEqualTo(0)
442             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE))
443                     .isEqualTo(0)
444             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0)
445             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
446             assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
447 
448             assertThat(op.getForegroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
449             assertThat(op.getBackgroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
450             assertThat(op.getRejectCount(AppOpsManager.UID_STATE_TOP,
451                     AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAGS_ALL))
452                     .isEqualTo(0)
453             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0)
454             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(0)
455             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION))
456                     .isEqualTo(0)
457             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE))
458                     .isEqualTo(0)
459             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0)
460             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
461             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
462         } finally {
463             setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid, AppOpsManager.MODE_FOREGROUND)
464             setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000, AppOpsManager.MODE_FOREGROUND)
465         }
466     }
467 
468     private fun createDataChunk(): HistoricalOps {
469         val chunk = HistoricalOps(SNAPSHOT_INTERVAL_MILLIS / 4,
470                 SNAPSHOT_INTERVAL_MILLIS / 2)
471         chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
472                 AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
473         chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
474                 AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
475         chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
476                 AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
477         chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
478                 AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
479         chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
480                 AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
481         chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid, packageName, null,
482                 AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
483         return chunk
484     }
485 
486     private fun setHistoryParameters(
487         mode: Int,
488         baseSnapshotInterval: Long,
489         compressionStep: Int
490     ) {
491         runWithShellPermissionIdentity {
492             appOpsManager.setHistoryParameters(mode, baseSnapshotInterval, compressionStep)
493         }
494     }
495 
496     private fun setUidMode(appOp: String, uid: Int, mode: Int) {
497         runWithShellPermissionIdentity {
498             appOpsManager.setUidMode(appOp, uid, mode)
499         }
500     }
501 
502     private fun addHistoricalOps(ops: AppOpsManager.HistoricalOps) {
503         runWithShellPermissionIdentity {
504             appOpsManager.addHistoricalOps(ops)
505         }
506     }
507 
508     private fun getHistoricalOps(
509         appOpsManager: AppOpsManager,
510         uid: Int = Process.INVALID_UID,
511         packageName: String? = null,
512         opNames: List<String>? = null,
513         beginTimeMillis: Long = 0,
514         endTimeMillis: Long = Long.MAX_VALUE
515     ): HistoricalOps? {
516         uiAutomation.adoptShellPermissionIdentity()
517         val array = arrayOfNulls<HistoricalOps>(1)
518         val lock = ReentrantLock()
519         val condition = lock.newCondition()
520         try {
521             lock.lock()
522             val request = AppOpsManager.HistoricalOpsRequest.Builder(
523                     beginTimeMillis, endTimeMillis)
524                     .setUid(uid)
525                     .setPackageName(packageName)
526                     .setOpNames(opNames?.toList())
527                     .build()
528             appOpsManager.getHistoricalOps(request, context.mainExecutor, Consumer { ops ->
529                 array[0] = ops
530                 try {
531                     lock.lock()
532                     condition.signalAll()
533                 } finally {
534                     lock.unlock()
535                 }
536             })
537             condition.await(5, TimeUnit.SECONDS)
538             return array[0]
539         } finally {
540             lock.unlock()
541             uiAutomation.dropShellPermissionIdentity()
542         }
543     }
544 
545     private fun assertHasCounts(ops: HistoricalOps, count: Long) {
546         assertThat(ops).isNotNull()
547 
548         if (count <= 0) {
549             assertThat(ops.uidCount).isEqualTo(0)
550             return
551         }
552 
553         assertThat(ops.uidCount).isEqualTo(1)
554 
555         val uidOps = ops.getUidOpsAt(0)
556         assertThat(uidOps).isNotNull()
557 
558         val packageOps = uidOps.getPackageOpsAt(0)
559         assertThat(packageOps).isNotNull()
560 
561         val op = packageOps.getOpAt(0)
562         assertThat(op).isNotNull()
563 
564         assertThat(op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
565         assertThat(op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
566         assertThat(op.getForegroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
567         assertThat(op.getBackgroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
568         assertThat(op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
569         assertThat(op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count)
570     }
571 
572     private fun getAccessCount(op: HistoricalOp, uidState: Int): Long {
573         return op.getAccessCount(uidState, uidState, AppOpsManager.OP_FLAGS_ALL)
574     }
575 
576     private fun getRejectCount(op: HistoricalOp, uidState: Int): Long {
577         return op.getRejectCount(uidState, uidState, AppOpsManager.OP_FLAGS_ALL)
578     }
579 
580     private fun getAccessDuration(op: HistoricalOp, uidState: Int): Long {
581         return op.getAccessDuration(uidState, uidState, AppOpsManager.OP_FLAGS_ALL)
582     }
583 
584     private fun getHistoricalOpsFromDiskRaw(
585         uid: Int,
586         packageName: String,
587         opNames: List<String>?,
588         beginTimeMillis: Long,
589         endTimeMillis: Long
590     ): HistoricalOps? {
591         uiAutomation.adoptShellPermissionIdentity()
592         val array = arrayOfNulls<HistoricalOps>(1)
593         val lock = ReentrantLock()
594         val condition = lock.newCondition()
595         try {
596             lock.lock()
597             val request = AppOpsManager.HistoricalOpsRequest.Builder(
598                     beginTimeMillis, endTimeMillis)
599                     .setUid(uid)
600                     .setPackageName(packageName)
601                     .setOpNames(opNames?.toList())
602                     .build()
603             appOpsManager.getHistoricalOpsFromDiskRaw(request, context.mainExecutor,
604                     Consumer { ops ->
605                         array[0] = ops
606                         try {
607                             lock.lock()
608                             condition.signalAll()
609                         } finally {
610                             lock.unlock()
611                         }
612                     })
613             condition.await(5, TimeUnit.SECONDS)
614             return array[0]
615         } finally {
616             lock.unlock()
617             uiAutomation.dropShellPermissionIdentity()
618         }
619     }
620 
621     companion object {
622         const val INTERVAL_COMPRESSION_MULTIPLIER = 10
623         const val SNAPSHOT_INTERVAL_MILLIS = 1000L
624 
625         val instrumentation get() = InstrumentationRegistry.getInstrumentation()
626         val context get() = instrumentation.context
627         val uiAutomation get() = instrumentation.uiAutomation
628 
629         private fun computeIntervalDurationMillis(depth: Int): Long {
630             return Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(),
631                     (depth + 1).toDouble()).toLong() * SNAPSHOT_INTERVAL_MILLIS
632         }
633 
634         private fun computeSlotCount(depth: Int): Int {
635             var count = 0
636             for (i in 1..depth) {
637                 count += Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(), i.toDouble()).toInt()
638             }
639             return count
640         }
641 
642         private fun computeIntervalBeginRawMillis(depth: Int): Long {
643             var beginTimeMillis: Long = 0
644             for (i in 0 until depth + 1) {
645                 beginTimeMillis += Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(),
646                         i.toDouble()).toLong()
647             }
648             return beginTimeMillis * SNAPSHOT_INTERVAL_MILLIS
649         }
650     }
651 }
652