1 /* 2 * Copyright (C) 2021 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.os.cts; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNotEquals; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertThrows; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeNotNull; 27 28 import android.os.Flags; 29 import android.os.HandlerThread; 30 import android.os.PerformanceHintManager; 31 import android.os.PerformanceHintManager.Session; 32 import android.os.Process; 33 import android.os.WorkDuration; 34 import android.platform.test.annotations.AppModeSdkSandbox; 35 import android.platform.test.annotations.RequiresFlagsEnabled; 36 import android.platform.test.flag.junit.CheckFlagsRule; 37 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 38 import android.util.Log; 39 import android.view.Surface; 40 import android.view.SurfaceControl; 41 import android.view.cts.surfacevalidator.ASurfaceControlTestActivity; 42 43 import androidx.test.InstrumentationRegistry; 44 import androidx.test.core.app.ActivityScenario; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.compatibility.common.util.ApiTest; 48 49 import com.google.common.base.Strings; 50 51 import org.junit.Before; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.concurrent.CountDownLatch; 57 58 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 59 @RunWith(AndroidJUnit4.class) 60 public class PerformanceHintManagerTest { 61 private static final String TAG = "PerformanceHintManagerTest"; 62 63 private final long DEFAULT_TARGET_NS = 16666666L; 64 private PerformanceHintManager mPerformanceHintManager; 65 private ASurfaceControlTestActivity mActivity; 66 private ActivityScenario<ASurfaceControlTestActivity> mScenario; 67 private SurfaceControl mSurfaceControl; 68 makeSurfaceControl()69 private void makeSurfaceControl() { 70 mScenario = ActivityScenario.launch(ASurfaceControlTestActivity.class); 71 final CountDownLatch activityLatch = new CountDownLatch(1); 72 73 mScenario.onActivity( 74 activity -> { 75 mActivity = activity; 76 activityLatch.countDown(); 77 mSurfaceControl = new SurfaceControl.Builder() 78 .setParent(mActivity.getSurfaceControl()) 79 .setName("testsurface") 80 .setHidden(false) 81 .build(); 82 }); 83 84 try { 85 activityLatch.await(); 86 } catch (InterruptedException e) { 87 fail("Error while waiting for activity"); 88 } 89 } 90 91 @Rule 92 public final CheckFlagsRule mCheckFlagsRule = 93 DeviceFlagsValueProvider.createCheckFlagsRule(); 94 95 static { 96 System.loadLibrary("ctsos_jni"); 97 } 98 99 @Before setUp()100 public void setUp() { 101 mPerformanceHintManager = 102 InstrumentationRegistry.getInstrumentation().getContext().getSystemService( 103 PerformanceHintManager.class); 104 } 105 createSession()106 private Session createSession() { 107 return mPerformanceHintManager.createHintSession( 108 new int[]{Process.myPid()}, DEFAULT_TARGET_NS); 109 } 110 111 @Test testCreateHintSession()112 public void testCreateHintSession() { 113 Session a = createSession(); 114 Session b = createSession(); 115 if (a == null) { 116 assertNull(b); 117 } else if (b == null) { 118 assertNull(a); 119 } else { 120 assertNotEquals(a, b); 121 } 122 } 123 124 @Test testNativeCreateHintSession()125 public void testNativeCreateHintSession() { 126 final String failureMessage = nativeTestCreateHintSession(); 127 if (!Strings.isNullOrEmpty(failureMessage)) { 128 fail(failureMessage); 129 } 130 } 131 132 @Test testGetPreferredUpdateRateNanos()133 public void testGetPreferredUpdateRateNanos() { 134 if (createSession() != null) { 135 assertTrue(mPerformanceHintManager.getPreferredUpdateRateNanos() > 0); 136 } else { 137 assertEquals(-1, mPerformanceHintManager.getPreferredUpdateRateNanos()); 138 } 139 } 140 141 @Test testNativeGetPreferredUpdateRateNanos()142 public void testNativeGetPreferredUpdateRateNanos() { 143 final String failureMessage = nativeTestGetPreferredUpdateRateNanos(); 144 if (!Strings.isNullOrEmpty(failureMessage)) { 145 fail(failureMessage); 146 } 147 } 148 149 @Test testUpdateTargetWorkDuration()150 public void testUpdateTargetWorkDuration() { 151 Session s = createSession(); 152 assumeNotNull(s); 153 s.updateTargetWorkDuration(100); 154 } 155 156 @Test testNativeUpdateTargetWorkDuration()157 public void testNativeUpdateTargetWorkDuration() { 158 final String failureMessage = nativeUpdateTargetWorkDuration(); 159 if (!Strings.isNullOrEmpty(failureMessage)) { 160 fail(failureMessage); 161 } 162 } 163 164 @Test testUpdateTargetWorkDurationWithNegativeDuration()165 public void testUpdateTargetWorkDurationWithNegativeDuration() { 166 Session s = createSession(); 167 assumeNotNull(s); 168 assertThrows(IllegalArgumentException.class, () -> { 169 s.updateTargetWorkDuration(-1); 170 }); 171 } 172 173 @Test testNativeUpdateTargetWorkDurationWithNegativeDuration()174 public void testNativeUpdateTargetWorkDurationWithNegativeDuration() { 175 final String failureMessage = nativeUpdateTargetWorkDurationWithNegativeDuration(); 176 if (!Strings.isNullOrEmpty(failureMessage)) { 177 fail(failureMessage); 178 } 179 } 180 181 @Test testReportActualWorkDuration()182 public void testReportActualWorkDuration() { 183 Session s = createSession(); 184 assumeNotNull(s); 185 s.updateTargetWorkDuration(100); 186 s.reportActualWorkDuration(1); 187 s.reportActualWorkDuration(100); 188 s.reportActualWorkDuration(1000); 189 } 190 191 @Test testNativeReportActualWorkDuration()192 public void testNativeReportActualWorkDuration() { 193 final String failureMessage = nativeReportActualWorkDuration(); 194 if (!Strings.isNullOrEmpty(failureMessage)) { 195 fail(failureMessage); 196 } 197 } 198 199 @Test testReportActualWorkDurationWithIllegalArgument()200 public void testReportActualWorkDurationWithIllegalArgument() { 201 Session s = createSession(); 202 assumeNotNull(s); 203 s.updateTargetWorkDuration(100); 204 assertThrows(IllegalArgumentException.class, () -> { 205 s.reportActualWorkDuration(-1); 206 }); 207 } 208 209 @Test testNativeReportActualWorkDurationWithIllegalArgument()210 public void testNativeReportActualWorkDurationWithIllegalArgument() { 211 final String failureMessage = nativeReportActualWorkDurationWithIllegalArgument(); 212 if (!Strings.isNullOrEmpty(failureMessage)) { 213 fail(failureMessage); 214 } 215 } 216 217 @Test 218 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#sendHint"}) testSendHint()219 public void testSendHint() { 220 Session s = createSession(); 221 assumeNotNull(s); 222 s.sendHint(Session.CPU_LOAD_UP); 223 s.sendHint(Session.CPU_LOAD_RESET); 224 s.sendHint(Session.GPU_LOAD_UP); 225 s.sendHint(Session.GPU_LOAD_DOWN); 226 s.sendHint(Session.GPU_LOAD_RESET); 227 } 228 229 @Test 230 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#sendHint"}) testSendHintWithNegativeHint()231 public void testSendHintWithNegativeHint() { 232 Session s = createSession(); 233 assumeNotNull(s); 234 assertThrows(IllegalArgumentException.class, () -> { 235 s.sendHint(-1); 236 }); 237 } 238 239 @Test testCloseHintSession()240 public void testCloseHintSession() { 241 Session s = createSession(); 242 assumeNotNull(s); 243 s.close(); 244 } 245 246 private static final class SyncRunnable implements Runnable { 247 248 /** true if run is completed. */ 249 private boolean mHadCompleted; 250 SyncRunnable()251 SyncRunnable() {} 252 run()253 public void run() { 254 synchronized (this) { 255 mHadCompleted = true; 256 notifyAll(); 257 } 258 } 259 waitForComplete()260 public synchronized void waitForComplete() throws InterruptedException { 261 if (!mHadCompleted) { 262 wait(1000); 263 } 264 } 265 } 266 267 private static class TestHandlerThread extends HandlerThread { 268 private Runnable mTarget; 269 TestHandlerThread(Runnable target)270 TestHandlerThread(Runnable target) { 271 super("testSetThreadIdsHandlerThread"); 272 mTarget = target; 273 } 274 275 @Override onLooperPrepared()276 protected void onLooperPrepared() { 277 super.onLooperPrepared(); 278 mTarget.run(); 279 } 280 } 281 282 @Test 283 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#setThreads"}) testSetThreads()284 public void testSetThreads() { 285 Session s = createSession(); 286 assumeNotNull(s); 287 int[] oldTids = new int[]{Process.myPid()}; 288 assertArrayEquals(oldTids, s.getThreadIds()); 289 290 final SyncRunnable sr = new SyncRunnable(); 291 HandlerThread thread = new TestHandlerThread(sr); 292 thread.start(); 293 try { 294 sr.waitForComplete(); 295 } catch (InterruptedException e) { 296 Log.e(TAG, "Error happens when waiting for handler thread: " + e); 297 } 298 int[] newTids = new int[]{thread.getThreadId()}; 299 s.setThreads(newTids); 300 assertArrayEquals(newTids, s.getThreadIds()); 301 } 302 303 @Test 304 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#setThreads"}) testSetThreadsWithEmptyList()305 public void testSetThreadsWithEmptyList() { 306 Session s = createSession(); 307 assumeNotNull(s); 308 assertThrows(IllegalArgumentException.class, () -> { 309 s.setThreads(new int[]{}); 310 }); 311 } 312 313 @Test 314 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#setThreads"}) testSetThreadsWithInvalidTid()315 public void testSetThreadsWithInvalidTid() { 316 final String failureMessage = nativeTestSetThreadsWithInvalidTid(); 317 if (!Strings.isNullOrEmpty(failureMessage)) { 318 fail(failureMessage); 319 } 320 } 321 322 @Test 323 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#setPreferPowerEfficiency"}) testSetPreferPowerEfficiency()324 public void testSetPreferPowerEfficiency() { 325 Session s = createSession(); 326 assumeNotNull(s); 327 s.setPreferPowerEfficiency(false); 328 s.setPreferPowerEfficiency(true); 329 s.setPreferPowerEfficiency(true); 330 } 331 332 @Test 333 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#setPreferPowerEfficiency"}) testNativeSetPreferPowerEfficiency()334 public void testNativeSetPreferPowerEfficiency() { 335 final String failureMessage = nativeSetPreferPowerEfficiency(); 336 if (!Strings.isNullOrEmpty(failureMessage)) { 337 fail(failureMessage); 338 } 339 } 340 341 @Test 342 @RequiresFlagsEnabled(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) 343 @ApiTest(apis = {"android.os.PerformanceHintManager.Session#reportActualWorkDuration"}) testReportActualWorkDurationWithWorkDurationClass()344 public void testReportActualWorkDurationWithWorkDurationClass() { 345 Session s = createSession(); 346 assumeNotNull(s); 347 s.updateTargetWorkDuration(16); 348 WorkDuration workDuration = new WorkDuration(); 349 workDuration.setWorkPeriodStartTimestampNanos(1000); 350 workDuration.setActualTotalDurationNanos(15); 351 workDuration.setActualCpuDurationNanos(11); 352 workDuration.setActualGpuDurationNanos(8); 353 s.reportActualWorkDuration(workDuration); 354 } 355 356 @Test 357 // TODO(b/304828176): Support NDK API annotation. 358 @ApiTest(apis = {"APerformanceHint_reportActualWorkDuration2"}) testNativeReportActualWorkDuration2()359 public void testNativeReportActualWorkDuration2() { 360 final String resultMessage = nativeTestReportActualWorkDuration2(); 361 if (!Strings.isNullOrEmpty(resultMessage)) { 362 fail(resultMessage); 363 } 364 } 365 366 @Test 367 // TODO(b/304828176): Support NDK API annotation. 368 @ApiTest(apis = {"APerformanceHint_reportActualWorkDuration2"}) testNativeReportActualWorkDuration2WithIllegalArgument()369 public void testNativeReportActualWorkDuration2WithIllegalArgument() { 370 final String resultMessage = nativeTestReportActualWorkDuration2WithIllegalArgument(); 371 if (!Strings.isNullOrEmpty(resultMessage)) { 372 fail(resultMessage); 373 } 374 } 375 376 @Test 377 // TODO(b/304828176): Support NDK API annotation. 378 @ApiTest(apis = {"APerformanceHint_notifyWorkloadIncrease", 379 "APerformanceHint_notifyWorkloadReset"}) testNativeLoadHints()380 public void testNativeLoadHints() { 381 final String resultMessage = nativeTestLoadHints(); 382 if (!Strings.isNullOrEmpty(resultMessage)) { 383 fail(resultMessage); 384 } 385 } 386 387 @Test 388 // TODO(b/304828176): Support NDK API annotation. 389 @ApiTest(apis = {"APerformanceHint_borrowSessionFromJava"}) testNativeBorrowSessionFromJava()390 public void testNativeBorrowSessionFromJava() { 391 Session session = createSession(); 392 assumeNotNull(session); 393 long nativeSession = nativeBorrowSessionFromJava(session); 394 assertNotEquals(0, nativeSession); 395 } 396 testNativeCreateHintSessionUsingConfig()397 public void testNativeCreateHintSessionUsingConfig() { 398 final String resultMessage = nativeTestCreateHintSessionUsingConfig(); 399 if (!Strings.isNullOrEmpty(resultMessage)) { 400 fail(resultMessage); 401 } 402 } 403 404 @Test testNativeCreateGraphicsPipelineSessionOverLimit()405 public void testNativeCreateGraphicsPipelineSessionOverLimit() { 406 final String resultMessage = nativeTestCreateGraphicsPipelineSessionOverLimit(); 407 if (!Strings.isNullOrEmpty(resultMessage)) { 408 fail(resultMessage); 409 } 410 } 411 412 @Test testNativeSetNativeSurfaces()413 public void testNativeSetNativeSurfaces() { 414 makeSurfaceControl(); 415 416 Surface surface = new Surface(mSurfaceControl); 417 418 final String resultMessage = nativeTestSetNativeSurfaces(mSurfaceControl, surface); 419 if (!Strings.isNullOrEmpty(resultMessage)) { 420 fail(resultMessage); 421 } 422 } 423 424 @Test testNativeAutoSessionTiming()425 public void testNativeAutoSessionTiming() { 426 makeSurfaceControl(); 427 428 final String resultMessage = nativeTestAutoSessionTiming(mSurfaceControl); 429 if (!Strings.isNullOrEmpty(resultMessage)) { 430 fail(resultMessage); 431 } 432 } 433 nativeTestCreateGraphicsPipelineSessionOverLimit()434 private native String nativeTestCreateGraphicsPipelineSessionOverLimit(); nativeTestCreateHintSession()435 private native String nativeTestCreateHintSession(); nativeTestCreateHintSessionUsingConfig()436 private native String nativeTestCreateHintSessionUsingConfig(); nativeTestGetMaxGraphicsPipelineThreadsCount()437 private native String nativeTestGetMaxGraphicsPipelineThreadsCount(); nativeTestGetPreferredUpdateRateNanos()438 private native String nativeTestGetPreferredUpdateRateNanos(); nativeUpdateTargetWorkDuration()439 private native String nativeUpdateTargetWorkDuration(); nativeUpdateTargetWorkDurationWithNegativeDuration()440 private native String nativeUpdateTargetWorkDurationWithNegativeDuration(); nativeReportActualWorkDuration()441 private native String nativeReportActualWorkDuration(); nativeReportActualWorkDurationWithIllegalArgument()442 private native String nativeReportActualWorkDurationWithIllegalArgument(); nativeTestSetThreadsWithInvalidTid()443 private native String nativeTestSetThreadsWithInvalidTid(); nativeSetPreferPowerEfficiency()444 private native String nativeSetPreferPowerEfficiency(); nativeTestReportActualWorkDuration2()445 private native String nativeTestReportActualWorkDuration2(); nativeTestReportActualWorkDuration2WithIllegalArgument()446 private native String nativeTestReportActualWorkDuration2WithIllegalArgument(); nativeTestLoadHints()447 private native String nativeTestLoadHints(); nativeBorrowSessionFromJava(Session session)448 private native long nativeBorrowSessionFromJava(Session session); nativeTestSetNativeSurfaces( SurfaceControl surfaceControl, Surface surface)449 private native String nativeTestSetNativeSurfaces( 450 SurfaceControl surfaceControl, Surface surface); nativeTestAutoSessionTiming(SurfaceControl surfaceControl)451 private native String nativeTestAutoSessionTiming(SurfaceControl surfaceControl); 452 } 453