1 /* 2 * Copyright 2019 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 androidx.benchmark 18 19 import android.app.Activity 20 import android.app.Application 21 import android.content.Intent 22 import android.os.Build 23 import android.os.Bundle 24 import android.os.Process 25 import android.util.Log 26 import android.view.WindowManager 27 import android.widget.TextView 28 import androidx.annotation.AnyThread 29 import androidx.annotation.RestrictTo 30 import androidx.annotation.WorkerThread 31 import androidx.test.platform.app.InstrumentationRegistry 32 import java.util.concurrent.atomic.AtomicReference 33 import kotlin.concurrent.thread 34 35 /** 36 * Simple opaque activity used to reduce benchmark interference from other windows. 37 * 38 * For example, sources of potential interference: 39 * - live wallpaper rendering 40 * - homescreen widget updates 41 * - hotword detection 42 * - status bar repaints 43 * - running in background (some cores may be foreground-app only) 44 */ 45 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 46 public class IsolationActivity : android.app.Activity() { 47 private var destroyed = false 48 onCreatenull49 override fun onCreate(savedInstanceState: Bundle?) { 50 super.onCreate(savedInstanceState) 51 setContentView(R.layout.isolation_activity) 52 53 // disable launch animation 54 @Suppress("Deprecation") overridePendingTransition(0, 0) 55 56 if (firstInit) { 57 if (!CpuInfo.locked && isSustainedPerformanceModeSupported()) { 58 sustainedPerformanceModeInUse = true 59 } 60 application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks) 61 62 // trigger the one missed lifecycle event, from registering the callbacks late 63 activityLifecycleCallbacks.onActivityCreated(this, savedInstanceState) 64 65 if (sustainedPerformanceModeInUse) { 66 // Keep at least one core busy. Together with a single threaded benchmark, this 67 // makes the process get multi-threaded setSustainedPerformanceMode. 68 // 69 // We want to keep to the relatively lower clocks of the multi-threaded benchmark 70 // mode to avoid any benchmarks running at higher clocks than any others. 71 // 72 // Note, thread names have 15 char max in Systrace 73 thread(name = "BenchSpinThread") { 74 Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST) 75 while (true) {} 76 } 77 } 78 firstInit = false 79 } 80 81 val old = singleton.getAndSet(this) 82 if (old != null && !old.destroyed && !old.isFinishing) { 83 throw IllegalStateException("Only one IsolationActivity should exist") 84 } 85 86 findViewById<TextView>(R.id.clock_state).text = 87 when { 88 CpuInfo.locked -> "Locked Clocks" 89 sustainedPerformanceModeInUse -> "Sustained Performance Mode" 90 else -> "" 91 } 92 } 93 onResumenull94 override fun onResume() { 95 super.onResume() 96 resumed = true 97 } 98 onPausenull99 override fun onPause() { 100 super.onPause() 101 resumed = false 102 } 103 onDestroynull104 override fun onDestroy() { 105 super.onDestroy() 106 destroyed = true 107 } 108 109 /** finish is ignored! we defer until [actuallyFinish] is called. */ finishnull110 override fun finish() {} 111 actuallyFinishnull112 public fun actuallyFinish() { 113 // disable close animation 114 @Suppress("Deprecation") overridePendingTransition(0, 0) 115 super.finish() 116 } 117 118 public companion object { 119 private const val TAG = "Benchmark" 120 internal val singleton = AtomicReference<IsolationActivity>() 121 private var firstInit = true 122 internal var sustainedPerformanceModeInUse = false 123 private set 124 125 public var resumed: Boolean = false 126 private set 127 128 @WorkerThread launchSingletonnull129 public fun launchSingleton() { 130 val intent = 131 Intent(Intent.ACTION_MAIN).apply { 132 Log.d(TAG, "launching Benchmark IsolationActivity") 133 setClassName( 134 InstrumentationRegistry.getInstrumentation().targetContext.packageName, 135 IsolationActivity::class.java.name 136 ) 137 addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 138 addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) 139 } 140 InstrumentationRegistry.getInstrumentation().startActivitySync(intent) 141 } 142 143 @AnyThread finishSingletonnull144 public fun finishSingleton() { 145 Log.d(TAG, "Benchmark runner being destroyed, tearing down activities") 146 singleton.getAndSet(null)?.apply { runOnUiThread { actuallyFinish() } } 147 } 148 isSustainedPerformanceModeSupportednull149 internal fun isSustainedPerformanceModeSupported(): Boolean = 150 if (Build.VERSION.SDK_INT >= 24) { 151 InstrumentationRegistry.getInstrumentation().isSustainedPerformanceModeSupported() 152 } else { 153 false 154 } 155 156 private val activityLifecycleCallbacks = 157 object : Application.ActivityLifecycleCallbacks { onActivityCreatednull158 override fun onActivityCreated(activity: Activity, bundle: Bundle?) { 159 if (sustainedPerformanceModeInUse && Build.VERSION.SDK_INT >= 24) { 160 activity.setSustainedPerformanceMode(true) 161 } 162 163 // Forcibly wake the device, and keep the screen on to prevent benchmark 164 // failures. 165 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { 166 activity.requestDismissKeyguard() 167 activity.setShowWhenLocked() 168 activity.setTurnScreenOn() 169 activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) 170 } else { 171 @Suppress("DEPRECATION") 172 activity.window.addFlags( 173 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or 174 WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or 175 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or 176 WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON 177 ) 178 } 179 } 180 onActivityDestroyednull181 override fun onActivityDestroyed(activity: Activity) {} 182 onActivitySaveInstanceStatenull183 override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {} 184 onActivityStartednull185 override fun onActivityStarted(activity: Activity) {} 186 onActivityStoppednull187 override fun onActivityStopped(activity: Activity) {} 188 onActivityPausednull189 override fun onActivityPaused(activity: Activity) {} 190 onActivityResumednull191 override fun onActivityResumed(activity: Activity) {} 192 } 193 } 194 } 195