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.server.wm.display; 18 19 import static android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry; 20 import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible; 21 import static android.server.wm.UiDeviceUtils.pressUnlockButton; 22 import static android.server.wm.UiDeviceUtils.pressWakeupButton; 23 import static android.server.wm.WindowManagerState.STATE_RESUMED; 24 import static android.view.Display.DEFAULT_DISPLAY; 25 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 26 import static android.view.cts.surfacevalidator.ASurfaceControlTestActivity.WAIT_TIMEOUT_S; 27 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS; 28 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM; 29 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; 30 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS; 31 import static android.widget.LinearLayout.VERTICAL; 32 33 import static org.junit.Assert.assertArrayEquals; 34 import static org.junit.Assert.assertEquals; 35 import static org.junit.Assert.assertNotEquals; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertNull; 38 39 import android.app.Activity; 40 import android.app.Instrumentation; 41 import android.app.KeyguardManager; 42 import android.content.ComponentName; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.graphics.Color; 46 import android.graphics.Point; 47 import android.graphics.Rect; 48 import android.os.Bundle; 49 import android.os.PowerManager; 50 import android.os.SystemClock; 51 import android.platform.test.annotations.Presubmit; 52 import android.server.wm.WindowManagerState; 53 import android.server.wm.WindowManagerStateHelper; 54 import android.service.displayhash.DisplayHashParams; 55 import android.util.Size; 56 import android.view.Gravity; 57 import android.view.SurfaceControl; 58 import android.view.View; 59 import android.view.ViewTreeObserver; 60 import android.view.WindowManager; 61 import android.view.displayhash.DisplayHash; 62 import android.view.displayhash.DisplayHashManager; 63 import android.view.displayhash.DisplayHashResultCallback; 64 import android.view.displayhash.VerifiedDisplayHash; 65 import android.widget.LinearLayout; 66 import android.widget.RelativeLayout; 67 68 import androidx.annotation.NonNull; 69 import androidx.test.filters.FlakyTest; 70 import androidx.test.platform.app.InstrumentationRegistry; 71 import androidx.test.rule.ActivityTestRule; 72 73 import com.android.compatibility.common.util.SystemUtil; 74 75 import org.junit.After; 76 import org.junit.Before; 77 import org.junit.Rule; 78 import org.junit.Test; 79 80 import java.util.ArrayList; 81 import java.util.Objects; 82 import java.util.Set; 83 import java.util.concurrent.CountDownLatch; 84 import java.util.concurrent.Executor; 85 import java.util.concurrent.TimeUnit; 86 87 @Presubmit 88 public class DisplayHashManagerTest { 89 private static final int WAIT_TIME_S = 5; 90 private static final int TIMEOUT_MS = 1000; 91 private static final int SLEEP_TIMEOUT_MS = 200; 92 93 private final Point mCenter = new Point(); 94 private final Point mTestViewSize = new Point(200, 300); 95 96 private Instrumentation mInstrumentation; 97 private RelativeLayout mMainView; 98 private TestActivity mActivity; 99 100 private View mTestView; 101 102 private DisplayHashManager mDisplayHashManager; 103 private String mPhashAlgorithm; 104 105 private Executor mExecutor; 106 107 private SyncDisplayHashResultCallback mSyncDisplayHashResultCallback; 108 109 protected WindowManagerStateHelper mWmState = new WindowManagerStateHelper(); 110 111 @Rule 112 public ActivityTestRule<TestActivity> mActivityRule = 113 new ActivityTestRule<>(TestActivity.class); 114 115 @Before setUp()116 public void setUp() throws Exception { 117 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 118 final Context context = mInstrumentation.getContext(); 119 final KeyguardManager km = context.getSystemService(KeyguardManager.class); 120 Intent intent = new Intent(Intent.ACTION_MAIN); 121 intent.setClass(context, TestActivity.class); 122 mActivity = mActivityRule.getActivity(); 123 124 if (km != null && km.isKeyguardLocked() || !Objects.requireNonNull( 125 context.getSystemService(PowerManager.class)).isInteractive()) { 126 pressWakeupButton(); 127 pressUnlockButton(); 128 } 129 130 mActivity.runOnUiThread(() -> { 131 mMainView = new RelativeLayout(mActivity); 132 mActivity.setContentView(mMainView); 133 }); 134 mInstrumentation.waitForIdleSync(); 135 mActivity.runOnUiThread(() -> { 136 mCenter.set((mMainView.getWidth() - mTestViewSize.x) / 2, 137 (mMainView.getHeight() - mTestViewSize.y) / 2); 138 }); 139 mDisplayHashManager = context.getSystemService(DisplayHashManager.class); 140 141 Set<String> algorithms = mDisplayHashManager.getSupportedHashAlgorithms(); 142 assertNotNull(algorithms); 143 assertNotEquals(0, algorithms.size()); 144 for (String algorithm : algorithms) { 145 if ("pHash".equalsIgnoreCase(algorithm)) { 146 mPhashAlgorithm = algorithm; 147 break; 148 } 149 } 150 assertNotNull(mPhashAlgorithm); 151 152 mExecutor = context.getMainExecutor(); 153 mSyncDisplayHashResultCallback = new SyncDisplayHashResultCallback(); 154 SystemUtil.runWithShellPermissionIdentity( 155 () -> mDisplayHashManager.setDisplayHashThrottlingEnabled(false)); 156 } 157 158 @After tearDown()159 public void tearDown() { 160 SystemUtil.runWithShellPermissionIdentity( 161 () -> mDisplayHashManager.setDisplayHashThrottlingEnabled(true)); 162 } 163 164 @FlakyTest(bugId = 292291447) 165 @Test testGenerateAndVerifyDisplayHash()166 public void testGenerateAndVerifyDisplayHash() throws InterruptedException { 167 setupChildView(); 168 169 // A solid color image has expected hash of all 0s 170 byte[] expectedImageHash = new byte[8]; 171 172 DisplayHash displayHash = generateDisplayHash(null); 173 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 174 displayHash); 175 assertNotNull(verifiedDisplayHash); 176 177 assertEquals(mTestViewSize.x, verifiedDisplayHash.getBoundsInWindow().width()); 178 assertEquals(mTestViewSize.y, verifiedDisplayHash.getBoundsInWindow().height()); 179 assertArrayEquals(expectedImageHash, verifiedDisplayHash.getImageHash()); 180 } 181 182 @Test testGenerateAndVerifyDisplayHash_BoundsInView()183 public void testGenerateAndVerifyDisplayHash_BoundsInView() throws InterruptedException { 184 setupChildView(); 185 186 Rect bounds = new Rect(10, 20, mTestViewSize.x / 2, mTestViewSize.y / 2); 187 DisplayHash displayHash = generateDisplayHash(new Rect(bounds)); 188 189 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 190 displayHash); 191 assertNotNull(verifiedDisplayHash); 192 assertEquals(bounds.width(), verifiedDisplayHash.getBoundsInWindow().width()); 193 assertEquals(bounds.height(), verifiedDisplayHash.getBoundsInWindow().height()); 194 } 195 196 @Test testGenerateAndVerifyDisplayHash_EmptyBounds()197 public void testGenerateAndVerifyDisplayHash_EmptyBounds() throws InterruptedException { 198 setupChildView(); 199 200 mTestView.generateDisplayHash(mPhashAlgorithm, new Rect(), mExecutor, 201 mSyncDisplayHashResultCallback); 202 203 int errorCode = mSyncDisplayHashResultCallback.getError(); 204 assertEquals(DISPLAY_HASH_ERROR_INVALID_BOUNDS, errorCode); 205 } 206 207 @Test testGenerateAndVerifyDisplayHash_BoundsBiggerThanView()208 public void testGenerateAndVerifyDisplayHash_BoundsBiggerThanView() 209 throws InterruptedException { 210 setupChildView(); 211 212 Rect bounds = new Rect(0, 0, mTestViewSize.x + 100, mTestViewSize.y + 100); 213 214 DisplayHash displayHash = generateDisplayHash(new Rect(bounds)); 215 216 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 217 displayHash); 218 assertNotNull(verifiedDisplayHash); 219 assertEquals(mTestViewSize.x, verifiedDisplayHash.getBoundsInWindow().width()); 220 assertEquals(mTestViewSize.y, verifiedDisplayHash.getBoundsInWindow().height()); 221 } 222 223 @Test testGenerateDisplayHash_BoundsOutOfView()224 public void testGenerateDisplayHash_BoundsOutOfView() throws InterruptedException { 225 setupChildView(); 226 227 Rect bounds = new Rect(mTestViewSize.x + 1, mTestViewSize.y + 1, mTestViewSize.x + 100, 228 mTestViewSize.y + 100); 229 230 mTestView.generateDisplayHash(mPhashAlgorithm, new Rect(bounds), 231 mExecutor, mSyncDisplayHashResultCallback); 232 int errorCode = mSyncDisplayHashResultCallback.getError(); 233 assertEquals(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN, errorCode); 234 } 235 236 @Test testGenerateDisplayHash_ViewOffscreen()237 public void testGenerateDisplayHash_ViewOffscreen() { 238 final CountDownLatch committedCallbackLatch = new CountDownLatch(1); 239 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 240 t.addTransactionCommittedListener(mExecutor, committedCallbackLatch::countDown); 241 242 mInstrumentation.runOnMainSync(() -> { 243 final RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(mTestViewSize.x, 244 mTestViewSize.y); 245 mTestView = new View(mActivity); 246 mTestView.setBackgroundColor(Color.BLUE); 247 mTestView.setX(-mTestViewSize.x); 248 249 mMainView.addView(mTestView, p); 250 mMainView.getRootSurfaceControl().applyTransactionOnDraw(t); 251 }); 252 mInstrumentation.waitForIdleSync(); 253 try { 254 committedCallbackLatch.await(WAIT_TIME_S, TimeUnit.SECONDS); 255 } catch (InterruptedException e) { 256 } 257 258 mTestView.generateDisplayHash(mPhashAlgorithm, null, mExecutor, 259 mSyncDisplayHashResultCallback); 260 261 int errorCode = mSyncDisplayHashResultCallback.getError(); 262 assertEquals(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN, errorCode); 263 } 264 265 @Test testGenerateDisplayHash_WindowOffscreen()266 public void testGenerateDisplayHash_WindowOffscreen() throws InterruptedException { 267 final WindowManager wm = mActivity.getWindowManager(); 268 final WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams(); 269 270 final CountDownLatch committedCallbackLatch = new CountDownLatch(1); 271 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 272 t.addTransactionCommittedListener(mExecutor, committedCallbackLatch::countDown); 273 mInstrumentation.runOnMainSync(() -> { 274 mMainView = new RelativeLayout(mActivity); 275 windowParams.width = mTestViewSize.x; 276 windowParams.height = mTestViewSize.y; 277 windowParams.gravity = Gravity.LEFT | Gravity.TOP; 278 windowParams.flags = FLAG_LAYOUT_NO_LIMITS; 279 mActivity.addWindow(mMainView, windowParams); 280 281 mMainView.getViewTreeObserver().addOnWindowAttachListener( 282 new ViewTreeObserver.OnWindowAttachListener() { 283 @Override 284 public void onWindowAttached() { 285 final RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams( 286 mTestViewSize.x, 287 mTestViewSize.y); 288 mTestView = new View(mActivity); 289 mTestView.setBackgroundColor(Color.BLUE); 290 mMainView.addView(mTestView, p); 291 mMainView.getRootSurfaceControl().applyTransactionOnDraw(t); 292 } 293 294 @Override 295 public void onWindowDetached() { 296 } 297 }); 298 }); 299 mInstrumentation.waitForIdleSync(); 300 try { 301 committedCallbackLatch.await(WAIT_TIME_S, TimeUnit.SECONDS); 302 } catch (InterruptedException e) { 303 } 304 305 waitForAllActivitiesResumed(); 306 307 generateDisplayHash(null); 308 309 mInstrumentation.runOnMainSync(() -> { 310 int[] mainViewLocationOnScreen = new int[2]; 311 mMainView.getLocationOnScreen(mainViewLocationOnScreen); 312 313 windowParams.x = -mTestViewSize.x - mainViewLocationOnScreen[0]; 314 wm.updateViewLayout(mMainView, windowParams); 315 }); 316 mInstrumentation.waitForIdleSync(); 317 waitForStableWindowGeometry(WAIT_TIMEOUT_S, TimeUnit.SECONDS); 318 319 mSyncDisplayHashResultCallback.reset(); 320 mTestView.generateDisplayHash(mPhashAlgorithm, null, mExecutor, 321 mSyncDisplayHashResultCallback); 322 323 int errorCode = mSyncDisplayHashResultCallback.getError(); 324 assertEquals(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN, errorCode); 325 } 326 327 @Test testGenerateDisplayHash_InvalidHashAlgorithm()328 public void testGenerateDisplayHash_InvalidHashAlgorithm() throws InterruptedException { 329 setupChildView(); 330 331 mTestView.generateDisplayHash("fake hash", null, mExecutor, 332 mSyncDisplayHashResultCallback); 333 int errorCode = mSyncDisplayHashResultCallback.getError(); 334 assertEquals(DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM, errorCode); 335 } 336 337 @Test testVerifyDisplayHash_ValidDisplayHash()338 public void testVerifyDisplayHash_ValidDisplayHash() throws InterruptedException { 339 setupChildView(); 340 341 DisplayHash displayHash = generateDisplayHash(null); 342 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 343 displayHash); 344 345 assertNotNull(verifiedDisplayHash); 346 assertEquals(displayHash.getTimeMillis(), verifiedDisplayHash.getTimeMillis()); 347 assertEquals(displayHash.getBoundsInWindow(), verifiedDisplayHash.getBoundsInWindow()); 348 assertEquals(displayHash.getHashAlgorithm(), verifiedDisplayHash.getHashAlgorithm()); 349 assertArrayEquals(displayHash.getImageHash(), verifiedDisplayHash.getImageHash()); 350 } 351 352 @Test testVerifyDisplayHash_InvalidDisplayHash()353 public void testVerifyDisplayHash_InvalidDisplayHash() throws InterruptedException { 354 setupChildView(); 355 356 DisplayHash displayHash = generateDisplayHash(null); 357 DisplayHash fakeDisplayHash = new DisplayHash( 358 displayHash.getTimeMillis(), displayHash.getBoundsInWindow(), 359 displayHash.getHashAlgorithm(), new byte[32], displayHash.getHmac()); 360 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 361 fakeDisplayHash); 362 363 assertNull(verifiedDisplayHash); 364 } 365 366 @Test testVerifiedDisplayHash()367 public void testVerifiedDisplayHash() { 368 long timeMillis = 1000; 369 Rect boundsInWindow = new Rect(0, 0, 50, 100); 370 String hashAlgorithm = "hashAlgorithm"; 371 byte[] imageHash = new byte[]{2, 4, 1, 5, 6, 2}; 372 VerifiedDisplayHash verifiedDisplayHash = new VerifiedDisplayHash(timeMillis, 373 boundsInWindow, hashAlgorithm, imageHash); 374 375 assertEquals(timeMillis, verifiedDisplayHash.getTimeMillis()); 376 assertEquals(boundsInWindow, verifiedDisplayHash.getBoundsInWindow()); 377 assertEquals(hashAlgorithm, verifiedDisplayHash.getHashAlgorithm()); 378 assertArrayEquals(imageHash, verifiedDisplayHash.getImageHash()); 379 } 380 381 @Test testGenerateDisplayHash_Throttle()382 public void testGenerateDisplayHash_Throttle() throws InterruptedException { 383 SystemUtil.runWithShellPermissionIdentity( 384 () -> mDisplayHashManager.setDisplayHashThrottlingEnabled(true)); 385 386 setupChildView(); 387 388 mTestView.generateDisplayHash(mPhashAlgorithm, null, mExecutor, 389 mSyncDisplayHashResultCallback); 390 mSyncDisplayHashResultCallback.getDisplayHash(); 391 mSyncDisplayHashResultCallback.reset(); 392 // Generate a second display hash right away. 393 mTestView.generateDisplayHash(mPhashAlgorithm, null, mExecutor, 394 mSyncDisplayHashResultCallback); 395 int errorCode = mSyncDisplayHashResultCallback.getError(); 396 assertEquals(DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS, errorCode); 397 } 398 399 @Test 400 @FlakyTest(bugId = 292291447) testGenerateAndVerifyDisplayHash_MultiColor()401 public void testGenerateAndVerifyDisplayHash_MultiColor() throws InterruptedException { 402 final CountDownLatch committedCallbackLatch = new CountDownLatch(1); 403 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 404 t.addTransactionCommittedListener(mExecutor, committedCallbackLatch::countDown); 405 mInstrumentation.runOnMainSync(() -> { 406 final RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(mTestViewSize.x, 407 mTestViewSize.y); 408 LinearLayout linearLayout = new LinearLayout(mActivity); 409 linearLayout.setOrientation(VERTICAL); 410 LinearLayout.LayoutParams blueParams = new LinearLayout.LayoutParams(mTestViewSize.x, 411 mTestViewSize.y / 2); 412 View blueView = new View(mActivity); 413 blueView.setBackgroundColor(Color.BLUE); 414 LinearLayout.LayoutParams redParams = new LinearLayout.LayoutParams(mTestViewSize.x, 415 mTestViewSize.y / 2); 416 View redView = new View(mActivity); 417 redView.setBackgroundColor(Color.RED); 418 419 linearLayout.addView(blueView, blueParams); 420 linearLayout.addView(redView, redParams); 421 mTestView = linearLayout; 422 423 mTestView.setX(mCenter.x); 424 mTestView.setY(mCenter.y); 425 mMainView.addView(mTestView, p); 426 mMainView.getRootSurfaceControl().applyTransactionOnDraw(t); 427 }); 428 mInstrumentation.waitForIdleSync(); 429 mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 430 try { 431 committedCallbackLatch.await(WAIT_TIME_S, TimeUnit.SECONDS); 432 } catch (InterruptedException e) { 433 } 434 435 ComponentName componentName = ComponentName.unflattenFromString( 436 "android.server.wm/android.server.wm.display.DisplayHashManagerTest$TestActivity"); 437 waitForActivityResumed(TIMEOUT_MS, componentName); 438 waitForWindowVisible(mTestView); 439 440 byte[] expectedImageHash = new byte[]{-1, -1, 127, -1, -1, -1, 127, 127}; 441 442 DisplayHash displayHash = generateDisplayHash(null); 443 VerifiedDisplayHash verifiedDisplayHash = mDisplayHashManager.verifyDisplayHash( 444 displayHash); 445 assertNotNull(verifiedDisplayHash); 446 447 assertEquals(mTestViewSize.x, verifiedDisplayHash.getBoundsInWindow().width()); 448 assertEquals(mTestViewSize.y, verifiedDisplayHash.getBoundsInWindow().height()); 449 assertArrayEquals(expectedImageHash, verifiedDisplayHash.getImageHash()); 450 } 451 waitForActivityResumed(int timeoutMs, ComponentName componentName)452 private void waitForActivityResumed(int timeoutMs, ComponentName componentName) { 453 long endTime = System.currentTimeMillis() + timeoutMs; 454 while (endTime > System.currentTimeMillis()) { 455 mWmState.computeState(); 456 if (mWmState.hasActivityState(componentName, STATE_RESUMED)) { 457 SystemClock.sleep(SLEEP_TIMEOUT_MS); 458 mWmState.computeState(); 459 break; 460 } 461 SystemClock.sleep(SLEEP_TIMEOUT_MS); 462 mWmState.computeState(); 463 } 464 } 465 466 @Test testDisplayHashParams()467 public void testDisplayHashParams() { 468 int width = 10; 469 int height = 20; 470 boolean isGrayscale = true; 471 DisplayHashParams displayHashParams = new DisplayHashParams.Builder() 472 .setBufferSize(width, height) 473 .setGrayscaleBuffer(isGrayscale) 474 .build(); 475 476 Size bufferSize = displayHashParams.getBufferSize(); 477 assertEquals(width, bufferSize.getWidth()); 478 assertEquals(height, bufferSize.getHeight()); 479 assertEquals(isGrayscale, displayHashParams.isGrayscaleBuffer()); 480 } 481 generateDisplayHash(Rect bounds)482 private DisplayHash generateDisplayHash(Rect bounds) { 483 mTestView.generateDisplayHash(mPhashAlgorithm, bounds, mExecutor, 484 mSyncDisplayHashResultCallback); 485 DisplayHash displayHash = mSyncDisplayHashResultCallback.getDisplayHash(); 486 487 assertNotNull(displayHash); 488 return displayHash; 489 } 490 setupChildView()491 private void setupChildView() throws InterruptedException { 492 final CountDownLatch committedCallbackLatch = new CountDownLatch(1); 493 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 494 t.addTransactionCommittedListener(mExecutor, committedCallbackLatch::countDown); 495 496 mInstrumentation.runOnMainSync(() -> { 497 final RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(mTestViewSize.x, 498 mTestViewSize.y); 499 mTestView = new View(mActivity); 500 mTestView.setX(mCenter.x); 501 mTestView.setY(mCenter.y); 502 mTestView.setBackgroundColor(Color.BLUE); 503 mMainView.addView(mTestView, p); 504 mMainView.getRootSurfaceControl().applyTransactionOnDraw(t); 505 }); 506 mInstrumentation.waitForIdleSync(); 507 try { 508 committedCallbackLatch.await(WAIT_TIME_S, TimeUnit.SECONDS); 509 } catch (InterruptedException e) { 510 } 511 waitForAllActivitiesResumed(); 512 waitForWindowVisible(mTestView); 513 } 514 515 public static class TestActivity extends Activity { 516 private final ArrayList<View> mViews = new ArrayList<>(); 517 518 @Override onCreate(Bundle savedInstanceState)519 protected void onCreate(Bundle savedInstanceState) { 520 super.onCreate(savedInstanceState); 521 } 522 addWindow(View view, WindowManager.LayoutParams attrs)523 void addWindow(View view, WindowManager.LayoutParams attrs) { 524 getWindowManager().addView(view, attrs); 525 mViews.add(view); 526 } 527 removeAllWindows()528 void removeAllWindows() { 529 for (View view : mViews) { 530 getWindowManager().removeViewImmediate(view); 531 } 532 mViews.clear(); 533 } 534 535 @Override onPause()536 protected void onPause() { 537 super.onPause(); 538 removeAllWindows(); 539 } 540 } 541 542 private static class SyncDisplayHashResultCallback implements DisplayHashResultCallback { 543 private static final int SCREENSHOT_WAIT_TIME_S = 1; 544 private DisplayHash mDisplayHash; 545 private int mError; 546 private CountDownLatch mCountDownLatch = new CountDownLatch(1); 547 reset()548 public void reset() { 549 mCountDownLatch = new CountDownLatch(1); 550 } 551 getDisplayHash()552 public DisplayHash getDisplayHash() { 553 try { 554 mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS); 555 } catch (Exception e) { 556 } 557 return mDisplayHash; 558 } 559 getError()560 public int getError() { 561 try { 562 mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS); 563 } catch (Exception e) { 564 } 565 return mError; 566 } 567 568 @Override onDisplayHashResult(@onNull DisplayHash displayHash)569 public void onDisplayHashResult(@NonNull DisplayHash displayHash) { 570 mDisplayHash = displayHash; 571 mCountDownLatch.countDown(); 572 } 573 574 @Override onDisplayHashError(int errorCode)575 public void onDisplayHashError(int errorCode) { 576 mError = errorCode; 577 mCountDownLatch.countDown(); 578 } 579 } 580 581 /** 582 * Waits for all activities to be resumed since image hash could be calculated before the 583 * activity is resumed. 584 */ waitForAllActivitiesResumed()585 private void waitForAllActivitiesResumed() { 586 mWmState.waitForWithAmState(WindowManagerState::allActivitiesResumed, 587 "All activities should be resumed"); 588 } 589 } 590