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