1 /* 2 * 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.Instrumentation 23 import android.content.Context 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.uiautomator.UiDevice 29 import androidx.test.runner.AndroidJUnit4 30 import com.google.common.truth.Truth.assertThat 31 import org.junit.After 32 import org.junit.Before 33 import org.junit.Ignore 34 import org.junit.Rule 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import java.util.ArrayList 38 import java.util.concurrent.TimeUnit 39 import java.util.concurrent.locks.ReentrantLock 40 import java.util.function.Consumer 41 42 @RunWith(AndroidJUnit4::class) 43 class HistoricalAppopsTest { 44 private val uid = Process.myUid() 45 private var appOpsManager: AppOpsManager? = null 46 private var packageName: String? = null 47 48 // Start an activity to make sure this app counts as being in the foreground 49 @Rule @JvmField 50 var activityRule = ActivityTestRule(UidStateForceActivity::class.java) 51 52 @Before 53 fun wakeScreenUp() { 54 val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) 55 device.wakeUp() 56 device.executeShellCommand("wm dismiss-keyguard") 57 } 58 59 @Before 60 fun setUpTest() { 61 appOpsManager = getContext().getSystemService(AppOpsManager::class.java) 62 packageName = getContext().packageName 63 val uiAutomation = getInstrumentation().getUiAutomation() 64 uiAutomation.adoptShellPermissionIdentity() 65 appOpsManager!!.clearHistory() 66 appOpsManager!!.resetHistoryParameters() 67 } 68 69 @After 70 fun tearDownTest() { 71 appOpsManager!!.clearHistory() 72 appOpsManager!!.resetHistoryParameters() 73 val uiAutomation = getInstrumentation().getUiAutomation() 74 uiAutomation.dropShellPermissionIdentity() 75 } 76 77 @Ignore("Feature is disabled in Android Q") 78 @Test 79 fun testGetHistoricalPackageOpsForegroundAccessInMemoryBucket() { 80 testGetHistoricalPackageOpsForegroundAtDepth(0) 81 } 82 83 @Ignore("Feature is disabled in Android Q") 84 @Test 85 fun testGetHistoricalPackageOpsForegroundAccessFirstOnDiskBucket() { 86 testGetHistoricalPackageOpsForegroundAtDepth(1) 87 } 88 89 @Ignore("Feature is disabled in Android Q") 90 @Test 91 fun testHistoricalAggregationOneLevelsDeep() { 92 testHistoricalAggregationSomeLevelsDeep(0) 93 } 94 95 @Ignore("Feature is disabled in Android Q") 96 @Test 97 fun testHistoricalAggregationTwoLevelsDeep() { 98 testHistoricalAggregationSomeLevelsDeep(1) 99 } 100 101 @Ignore("Feature is disabled in Android Q") 102 @Test 103 fun testHistoricalAggregationOverflow() { 104 // Configure historical registry behavior. 105 appOpsManager!!.setHistoryParameters( 106 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE, 107 SNAPSHOT_INTERVAL_MILLIS, 108 INTERVAL_COMPRESSION_MULTIPLIER) 109 110 // Add the data to the history 111 val chunk = createDataChunk() 112 val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3 113 for (i in 0 until chunkCount) { 114 appOpsManager!!.addHistoricalOps(chunk) 115 } 116 117 // Validate the data for the first interval 118 val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0) 119 val firstIntervalEndMillis = computeIntervalBeginRawMillis(1) 120 val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 121 null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis) 122 assertHasCounts(firstOps!!, 197) 123 124 // Validate the data for the second interval 125 val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1) 126 val secondIntervalEndMillis = computeIntervalBeginRawMillis(2) 127 val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 128 null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis) 129 assertHasCounts(secondOps!!, 33) 130 131 // Validate the data for both intervals 132 val thirdIntervalBeginMillis = firstIntervalEndMillis - SNAPSHOT_INTERVAL_MILLIS 133 val thirdIntervalEndMillis = secondIntervalBeginMillis + SNAPSHOT_INTERVAL_MILLIS 134 val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 135 null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis) 136 assertHasCounts(thirdOps!!, 33) 137 } 138 139 @Ignore("Feature is disabled in Android Q") 140 @Test 141 fun testHistoryTimeTravel() { 142 // Configure historical registry behavior. 143 appOpsManager!!.setHistoryParameters( 144 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE, 145 SNAPSHOT_INTERVAL_MILLIS, 146 INTERVAL_COMPRESSION_MULTIPLIER) 147 148 // Fill the first two intervals with data 149 val chunk = createDataChunk() 150 val chunkCount = computeSlotCount(2) * SNAPSHOT_INTERVAL_MILLIS / chunk.endTimeMillis 151 for (i in 0 until chunkCount) { 152 appOpsManager!!.addHistoricalOps(chunk) 153 } 154 155 // Move history in past with the first interval duration 156 val firstIntervalDurationMillis = computeIntervalDurationMillis(0) 157 appOpsManager!!.offsetHistory(firstIntervalDurationMillis) 158 159 // Validate the data for the first interval 160 val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0) 161 val firstIntervalEndMillis = firstIntervalBeginMillis + firstIntervalDurationMillis 162 val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 163 null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis) 164 assertThat(firstOps).isNotNull() 165 assertThat(firstOps!!.uidCount).isEqualTo(0) 166 167 // Validate the data for the second interval 168 val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1) 169 val secondIntervalDurationMillis = computeIntervalDurationMillis(1) 170 val secondIntervalEndMillis = secondIntervalBeginMillis + secondIntervalDurationMillis 171 val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 172 null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis) 173 val secondChunkCount = ((computeSlotCount(2) - computeSlotCount(1)) 174 .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) 175 assertHasCounts(secondOps!!, 10 * secondChunkCount) 176 177 // Validate the data for the third interval 178 val thirdIntervalBeginMillis = computeIntervalBeginRawMillis(2) 179 val thirdIntervalDurationMillis = computeIntervalDurationMillis(2) 180 val thirdIntervalEndMillis = thirdIntervalBeginMillis + thirdIntervalDurationMillis 181 val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 182 null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis) 183 val thirdChunkCount = secondChunkCount / INTERVAL_COMPRESSION_MULTIPLIER 184 assertHasCounts(thirdOps!!, 10 * thirdChunkCount) 185 186 // Move history in future with the first interval duration 187 appOpsManager!!.offsetHistory(- (2.5f * firstIntervalDurationMillis).toLong()) 188 189 // Validate the data for the first interval 190 val fourthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 191 null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis) 192 assertHasCounts(fourthOps!!, 194) 193 194 // Validate the data for the second interval 195 val fifthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 196 null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis) 197 198 assertThat(fifthOps).isNotNull() 199 assertHasCounts(fifthOps!!, 1703) 200 } 201 202 private fun testHistoricalAggregationSomeLevelsDeep(depth: Int) { 203 // Configure historical registry behavior. 204 appOpsManager!!.setHistoryParameters( 205 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE, 206 SNAPSHOT_INTERVAL_MILLIS, 207 INTERVAL_COMPRESSION_MULTIPLIER) 208 209 // Add the data to the history 210 val chunk = createDataChunk() 211 val chunkCount = (computeSlotCount(depth + 1) 212 .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) 213 for (i in 0 until chunkCount) { 214 appOpsManager!!.addHistoricalOps(chunk) 215 } 216 217 // Validate the data for the full interval 218 val intervalBeginMillis = computeIntervalBeginRawMillis(depth) 219 val intervalEndMillis = computeIntervalBeginRawMillis(depth + 1) 220 val ops = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!, 221 null /*opNames*/, intervalBeginMillis, intervalEndMillis) 222 val expectedOpCount = ((computeSlotCount(depth + 1) - computeSlotCount(depth)) 223 .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) * 10 224 assertHasCounts(ops!!, expectedOpCount) 225 } 226 227 private fun testGetHistoricalPackageOpsForegroundAtDepth(depth: Int) { 228 // Configure historical registry behavior. 229 appOpsManager!!.setHistoryParameters( 230 AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE, 231 SNAPSHOT_INTERVAL_MILLIS, 232 INTERVAL_COMPRESSION_MULTIPLIER) 233 234 appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid, 235 AppOpsManager.MODE_ALLOWED) 236 appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000, 237 AppOpsManager.MODE_ALLOWED) 238 239 activityRule.activity.waitForResumed() 240 241 try { 242 val noteCount = 5 243 244 var beginTimeMillis = 0L 245 var endTimeMillis = 0L 246 247 // Note ops such that we have data at all levels 248 for (d in depth downTo 0) { 249 for (i in 0 until noteCount) { 250 appOpsManager!!.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName) 251 } 252 253 if (d > 0) { 254 val previousIntervalDuration = computeIntervalDurationMillis(d - 2) 255 val currentIntervalDuration = computeIntervalDurationMillis(d - 1) 256 257 endTimeMillis -= previousIntervalDuration 258 beginTimeMillis -= currentIntervalDuration 259 260 val sleepDurationMillis = currentIntervalDuration / 2 261 SystemClock.sleep(sleepDurationMillis) 262 } 263 } 264 265 val nowMillis = System.currentTimeMillis() 266 if (depth > 0) { 267 beginTimeMillis += nowMillis 268 endTimeMillis += nowMillis 269 } else { 270 beginTimeMillis = nowMillis - SNAPSHOT_INTERVAL_MILLIS 271 endTimeMillis = Long.MAX_VALUE 272 } 273 274 // Get all ops for the package 275 val allOps = getHistoricalOps(appOpsManager!!, uid, packageName!!, 276 null, beginTimeMillis, endTimeMillis) 277 278 assertThat(allOps).isNotNull() 279 assertThat(allOps!!.uidCount).isEqualTo(1) 280 assertThat(allOps.beginTimeMillis).isEqualTo(beginTimeMillis) 281 assertThat(allOps.endTimeMillis).isGreaterThan(beginTimeMillis) 282 283 val uidOps = allOps.getUidOpsAt(0) 284 assertThat(uidOps).isNotNull() 285 assertThat(uidOps.uid).isEqualTo(Process.myUid()) 286 assertThat(uidOps.packageCount).isEqualTo(1) 287 288 val packageOps = uidOps.getPackageOpsAt(0) 289 assertThat(packageOps).isNotNull() 290 assertThat(packageOps.packageName).isEqualTo(getContext().packageName) 291 assertThat(packageOps.opCount).isEqualTo(1) 292 293 val op = packageOps.getOpAt(0) 294 assertThat(op).isNotNull() 295 assertThat(op.opName).isEqualTo(AppOpsManager.OPSTR_START_FOREGROUND) 296 297 assertThat(op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL)) 298 .isEqualTo(noteCount) 299 assertThat(op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0) 300 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0) 301 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(noteCount) 302 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION)) 303 .isEqualTo(0) 304 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE)) 305 .isEqualTo(0) 306 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0) 307 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0) 308 assertThat(getAccessCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0) 309 310 assertThat(op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0) 311 assertThat(op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0) 312 assertThat(op.getAccessDuration(AppOpsManager.UID_STATE_TOP, 313 AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAGS_ALL)) 314 .isEqualTo(0) 315 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0) 316 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(0) 317 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION)) 318 .isEqualTo(0) 319 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE)) 320 .isEqualTo(0) 321 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0) 322 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0) 323 assertThat(getAccessDuration(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0) 324 325 assertThat(op.getForegroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0) 326 assertThat(op.getBackgroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0) 327 assertThat(op.getRejectCount(AppOpsManager.UID_STATE_TOP, 328 AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAGS_ALL)) 329 .isEqualTo(0) 330 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_PERSISTENT)).isEqualTo(0) 331 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_TOP)).isEqualTo(0) 332 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE_LOCATION)) 333 .isEqualTo(0) 334 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND_SERVICE)) 335 .isEqualTo(0) 336 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_FOREGROUND)).isEqualTo(0) 337 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0) 338 assertThat(getRejectCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0) 339 } finally { 340 appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid, 341 AppOpsManager.MODE_FOREGROUND) 342 appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000, 343 AppOpsManager.MODE_FOREGROUND) 344 } 345 } 346 347 private fun createDataChunk(): HistoricalOps { 348 val chunk = HistoricalOps(SNAPSHOT_INTERVAL_MILLIS / 4, 349 SNAPSHOT_INTERVAL_MILLIS / 2) 350 chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid, 351 packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10) 352 chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid, 353 packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10) 354 chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid, 355 packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10) 356 chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid, 357 packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10) 358 chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid, 359 packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10) 360 chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid, 361 packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10) 362 return chunk 363 } 364 365 private fun getHistoricalOps(appOpsManager: AppOpsManager, uid: Int, 366 packageName: String, opNames: List<String>?, beginTimeMillis: Long, 367 endTimeMillis: Long): HistoricalOps? { 368 val array = arrayOfNulls<HistoricalOps>(1) 369 val lock = ReentrantLock() 370 val condition = lock.newCondition() 371 try { 372 lock.lock() 373 val request = AppOpsManager.HistoricalOpsRequest.Builder( 374 beginTimeMillis, endTimeMillis) 375 .setUid(uid) 376 .setPackageName(packageName) 377 .setOpNames(if (opNames != null) ArrayList(opNames) else null) 378 .build() 379 appOpsManager.getHistoricalOps(request, getContext().getMainExecutor(), 380 Consumer { ops -> 381 array[0] = ops 382 try { 383 lock.lock() 384 condition.signalAll() 385 } finally { 386 lock.unlock() 387 } 388 }) 389 condition.await(5, TimeUnit.SECONDS) 390 return array[0] 391 } finally { 392 lock.unlock() 393 } 394 } 395 396 private fun assertHasCounts(ops: HistoricalOps, count: Long) { 397 assertThat(ops).isNotNull() 398 assertThat(ops.uidCount).isEqualTo(1) 399 400 val uidOps = ops.getUidOpsAt(0) 401 assertThat(uidOps).isNotNull() 402 403 val packageOps = uidOps.getPackageOpsAt(0) 404 assertThat(packageOps).isNotNull() 405 406 val op = packageOps.getOpAt(0) 407 assertThat(op).isNotNull() 408 409 assertThat(op.getForegroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 410 assertThat(op.getBackgroundAccessCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 411 assertThat(op.getForegroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 412 assertThat(op.getBackgroundRejectCount(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 413 assertThat(op.getForegroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 414 assertThat(op.getBackgroundAccessDuration(AppOpsManager.OP_FLAGS_ALL)).isEqualTo(count) 415 } 416 417 private fun getAccessCount(op: HistoricalOp, uidState: Int): Long { 418 return op.getAccessCount(uidState, uidState, AppOpsManager.OP_FLAGS_ALL) 419 } 420 421 private fun getRejectCount(op: HistoricalOp, uidState: Int): Long { 422 return op.getRejectCount(uidState, uidState, AppOpsManager.OP_FLAGS_ALL) 423 } 424 425 private fun getAccessDuration(op: HistoricalOp, uidState: Int): Long { 426 return op.getAccessDuration(uidState, uidState, AppOpsManager.OP_FLAGS_ALL) 427 } 428 429 private fun getHistoricalOpsFromDiskRaw(appOpsManager: AppOpsManager, uid: Int, 430 packageName: String, opNames: List<String>?, beginTimeMillis: Long, 431 endTimeMillis: Long): HistoricalOps? { 432 val array = arrayOfNulls<HistoricalOps>(1) 433 val lock = ReentrantLock() 434 val condition = lock.newCondition() 435 try { 436 lock.lock() 437 val request = AppOpsManager.HistoricalOpsRequest.Builder( 438 beginTimeMillis, endTimeMillis) 439 .setUid(uid) 440 .setPackageName(packageName) 441 .setOpNames(if (opNames != null) ArrayList(opNames) else null) 442 .build() 443 appOpsManager.getHistoricalOpsFromDiskRaw(request, getContext().getMainExecutor(), 444 Consumer { ops -> 445 array[0] = ops 446 try { 447 lock.lock() 448 condition.signalAll() 449 } finally { 450 lock.unlock() 451 } 452 }) 453 condition.await(5, TimeUnit.SECONDS) 454 return array[0] 455 } finally { 456 lock.unlock() 457 } 458 } 459 460 companion object { 461 const val INTERVAL_COMPRESSION_MULTIPLIER = 10 462 const val SNAPSHOT_INTERVAL_MILLIS = 1000L 463 464 private fun computeIntervalDurationMillis(depth: Int): Long { 465 return Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(), 466 (depth + 1).toDouble()).toLong() * SNAPSHOT_INTERVAL_MILLIS 467 } 468 469 private fun computeSlotCount(depth: Int): Int { 470 var count = 0 471 for (i in 1..depth) { 472 count += Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(), i.toDouble()).toInt() 473 } 474 return count 475 } 476 477 private fun computeIntervalBeginRawMillis(depth: Int): Long { 478 var beginTimeMillis: Long = 0 479 for (i in 0 until depth + 1) { 480 beginTimeMillis += Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(), 481 i.toDouble()).toLong() 482 } 483 return beginTimeMillis * SNAPSHOT_INTERVAL_MILLIS 484 } 485 486 private fun getInstrumentation(): Instrumentation { 487 return InstrumentationRegistry.getInstrumentation() 488 } 489 490 private fun getContext(): Context { 491 return getInstrumentation().context 492 } 493 } 494 } 495