1 /* 2 * Copyright (C) 2022 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.window.extensions.area; 18 19 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.hardware.devicestate.DeviceStateManager; 24 import android.hardware.devicestate.DeviceStateRequest; 25 import android.hardware.display.DisplayManager; 26 import android.util.ArraySet; 27 import android.util.DisplayMetrics; 28 import android.util.Pair; 29 import android.view.Display; 30 import android.view.DisplayAddress; 31 import android.view.Surface; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 import androidx.window.extensions.WindowExtensions; 36 import androidx.window.extensions.core.util.function.Consumer; 37 38 import com.android.internal.R; 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.util.ArrayUtils; 42 43 import java.util.Objects; 44 import java.util.concurrent.Executor; 45 46 /** 47 * Reference implementation of androidx.window.extensions.area OEM interface for use with 48 * WindowManager Jetpack. 49 * 50 * This component currently supports Rear Display mode with the ability to add and remove 51 * status listeners for this mode. 52 * 53 * The public methods in this class are thread-safe. 54 **/ 55 public class WindowAreaComponentImpl implements WindowAreaComponent, 56 DeviceStateManager.DeviceStateCallback { 57 58 private static final int INVALID_DISPLAY_ADDRESS = -1; 59 private final Object mLock = new Object(); 60 61 @NonNull 62 private final DeviceStateManager mDeviceStateManager; 63 @NonNull 64 private final DisplayManager mDisplayManager; 65 @NonNull 66 private final Executor mExecutor; 67 68 @GuardedBy("mLock") 69 private final ArraySet<Consumer<Integer>> mRearDisplayStatusListeners = new ArraySet<>(); 70 @GuardedBy("mLock") 71 private final ArraySet<Consumer<ExtensionWindowAreaStatus>> 72 mRearDisplayPresentationStatusListeners = new ArraySet<>(); 73 private final int mRearDisplayState; 74 private final int mConcurrentDisplayState; 75 @NonNull 76 private final int[] mFoldedDeviceStates; 77 private long mRearDisplayAddress = INVALID_DISPLAY_ADDRESS; 78 @WindowAreaSessionState 79 private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE; 80 81 @GuardedBy("mLock") 82 private int mCurrentDeviceState = INVALID_DEVICE_STATE; 83 @GuardedBy("mLock") 84 private int[] mCurrentSupportedDeviceStates; 85 86 @GuardedBy("mLock") 87 private DeviceStateRequest mRearDisplayStateRequest; 88 @GuardedBy("mLock") 89 private RearDisplayPresentationController mRearDisplayPresentationController; 90 91 @Nullable 92 @GuardedBy("mLock") 93 private DisplayMetrics mRearDisplayMetrics; 94 95 @WindowAreaSessionState 96 @GuardedBy("mLock") 97 private int mLastReportedRearDisplayPresentationStatus; 98 WindowAreaComponentImpl(@onNull Context context)99 public WindowAreaComponentImpl(@NonNull Context context) { 100 mDeviceStateManager = context.getSystemService(DeviceStateManager.class); 101 mDisplayManager = context.getSystemService(DisplayManager.class); 102 mExecutor = context.getMainExecutor(); 103 104 mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedStates(); 105 mFoldedDeviceStates = context.getResources().getIntArray( 106 R.array.config_foldedDeviceStates); 107 108 // TODO(b/236022708) Move rear display state to device state config file 109 mRearDisplayState = context.getResources().getInteger( 110 R.integer.config_deviceStateRearDisplay); 111 112 mConcurrentDisplayState = context.getResources().getInteger( 113 R.integer.config_deviceStateConcurrentRearDisplay); 114 115 mDeviceStateManager.registerCallback(mExecutor, this); 116 mRearDisplayAddress = getRearDisplayAddress(context); 117 } 118 119 /** 120 * Adds a listener interested in receiving updates on the RearDisplayStatus 121 * of the device. Because this is being called from the OEM provided 122 * extensions, the result of the listener will be posted on the executor 123 * provided by the developer at the initial call site. 124 * 125 * Rear display mode moves the calling application to the display on the device that is 126 * facing the same direction as the rear cameras. This would be the cover display on a fold-in 127 * style device when the device is opened. 128 * 129 * Depending on the initial state of the device, the {@link Consumer} will receive either 130 * {@link WindowAreaComponent#STATUS_AVAILABLE} or 131 * {@link WindowAreaComponent#STATUS_UNAVAILABLE} if the feature is supported or not in that 132 * state respectively. When the rear display feature is triggered, the status is updated to be 133 * {@link WindowAreaComponent#STATUS_ACTIVE}. 134 * TODO(b/240727590): Prefix with AREA_ 135 * 136 * @param consumer {@link Consumer} interested in receiving updates to the status of 137 * rear display mode. 138 */ 139 @Override addRearDisplayStatusListener( @onNull Consumer<@WindowAreaStatus Integer> consumer)140 public void addRearDisplayStatusListener( 141 @NonNull Consumer<@WindowAreaStatus Integer> consumer) { 142 synchronized (mLock) { 143 mRearDisplayStatusListeners.add(consumer); 144 145 // If current device state is still invalid, the initial value has not been provided. 146 if (mCurrentDeviceState == INVALID_DEVICE_STATE) { 147 return; 148 } 149 consumer.accept(getCurrentRearDisplayModeStatus()); 150 } 151 } 152 153 /** 154 * Removes a listener no longer interested in receiving updates. 155 * @param consumer no longer interested in receiving updates to RearDisplayStatus 156 */ 157 @Override removeRearDisplayStatusListener( @onNull Consumer<@WindowAreaStatus Integer> consumer)158 public void removeRearDisplayStatusListener( 159 @NonNull Consumer<@WindowAreaStatus Integer> consumer) { 160 synchronized (mLock) { 161 mRearDisplayStatusListeners.remove(consumer); 162 } 163 } 164 165 /** 166 * Creates and starts a rear display session and provides updates to the 167 * callback provided. Because this is being called from the OEM provided 168 * extensions, the result of the listener will be posted on the executor 169 * provided by the developer at the initial call site. 170 * 171 * Rear display mode moves the calling application to the display on the device that is 172 * facing the same direction as the rear cameras. This would be the cover display on a fold-in 173 * style device when the device is opened. 174 * 175 * When rear display mode is enabled, a request is made to {@link DeviceStateManager} 176 * to override the device state to the state that corresponds to RearDisplay 177 * mode. When the {@link DeviceStateRequest} is activated, the provided {@link Consumer} is 178 * notified that the session is active by receiving 179 * {@link WindowAreaComponent#SESSION_STATE_ACTIVE}. 180 * 181 * @param activity to provide updates to the client on 182 * the status of the Session 183 * @param rearDisplaySessionCallback to provide updates to the client on 184 * the status of the Session 185 */ 186 @Override startRearDisplaySession(@onNull Activity activity, @NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback)187 public void startRearDisplaySession(@NonNull Activity activity, 188 @NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback) { 189 synchronized (mLock) { 190 if (mRearDisplayStateRequest != null) { 191 // Rear display session is already active 192 throw new IllegalStateException( 193 "Unable to start new rear display session as one is already active"); 194 } 195 mRearDisplayStateRequest = DeviceStateRequest.newBuilder(mRearDisplayState).build(); 196 mDeviceStateManager.requestState( 197 mRearDisplayStateRequest, 198 mExecutor, 199 new RearDisplayStateRequestCallbackAdapter(rearDisplaySessionCallback) 200 ); 201 } 202 } 203 204 /** 205 * Ends the current rear display session and provides updates to the 206 * callback provided. Because this is being called from the OEM provided 207 * extensions, the result of the listener will be posted on the executor 208 * provided by the developer at the initial call site. 209 */ 210 @Override endRearDisplaySession()211 public void endRearDisplaySession() { 212 synchronized (mLock) { 213 if (mRearDisplayStateRequest != null || isRearDisplayActive()) { 214 mRearDisplayStateRequest = null; 215 mDeviceStateManager.cancelStateRequest(); 216 } 217 } 218 } 219 220 /** 221 * Returns the{@link DisplayMetrics} associated with the rear facing display. If the rear facing 222 * display was not found in the display list, but we have already computed the 223 * {@link DisplayMetrics} for that display, we return the cached value. If no display has been 224 * found, then we return an empty {@link DisplayMetrics} value. 225 * 226 * TODO(b/267563768): Update with guidance from Display team for missing displays. 227 * 228 * @since {@link WindowExtensions#VENDOR_API_LEVEL_3} 229 */ 230 @Override 231 @NonNull getRearDisplayMetrics()232 public DisplayMetrics getRearDisplayMetrics() { 233 DisplayMetrics rearDisplayMetrics = null; 234 235 // DISPLAY_CATEGORY_REAR displays are only available when you are in the concurrent 236 // display state, so we have to look through all displays to match the address 237 final Display[] displays = mDisplayManager.getDisplays( 238 DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); 239 240 241 for (int i = 0; i < displays.length; i++) { 242 DisplayAddress.Physical address = 243 (DisplayAddress.Physical) displays[i].getAddress(); 244 if (mRearDisplayAddress == address.getPhysicalDisplayId()) { 245 rearDisplayMetrics = new DisplayMetrics(); 246 final Display rearDisplay = displays[i]; 247 248 // We must always retrieve the metrics for the rear display regardless of if it is 249 // the default display or not. 250 rearDisplay.getRealMetrics(rearDisplayMetrics); 251 252 // TODO(b/287170025): This should be something like if (!rearDisplay.isEnabled) 253 // instead. Currently when the rear display is disabled, its state is STATE_OFF. 254 if (rearDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) { 255 final Display defaultDisplay = mDisplayManager 256 .getDisplay(Display.DEFAULT_DISPLAY); 257 rotateRearDisplayMetricsIfNeeded(defaultDisplay.getRotation(), 258 rearDisplay.getRotation(), rearDisplayMetrics); 259 } 260 break; 261 } 262 } 263 264 synchronized (mLock) { 265 // Update the rear display metrics with our latest value if one was received 266 if (rearDisplayMetrics != null) { 267 mRearDisplayMetrics = rearDisplayMetrics; 268 } 269 270 return Objects.requireNonNullElseGet(mRearDisplayMetrics, DisplayMetrics::new); 271 } 272 } 273 274 /** 275 * Adds a listener interested in receiving updates on the RearDisplayPresentationStatus 276 * of the device. Because this is being called from the OEM provided 277 * extensions, the result of the listener will be posted on the executor 278 * provided by the developer at the initial call site. 279 * 280 * Rear display presentation mode is a feature where an {@link Activity} can present 281 * additional content on a device with a second display that is facing the same direction 282 * as the rear camera (i.e. the cover display on a fold-in style device). The calling 283 * {@link Activity} does not move, whereas in rear display mode it does. 284 * 285 * This listener receives a {@link Pair} with the first item being the 286 * {@link WindowAreaComponent.WindowAreaStatus} that corresponds to the current status of the 287 * feature, and the second being the {@link DisplayMetrics} of the display that would be 288 * presented to when the feature is active. 289 * 290 * Depending on the initial state of the device, the {@link Consumer} will receive either 291 * {@link WindowAreaComponent#STATUS_AVAILABLE} or 292 * {@link WindowAreaComponent#STATUS_UNAVAILABLE} for the status value of the {@link Pair} if 293 * the feature is supported or not in that state respectively. Rear display presentation mode is 294 * currently not supported when the device is folded. When the rear display presentation feature 295 * is triggered, the status is updated to be {@link WindowAreaComponent#STATUS_UNAVAILABLE}. 296 * TODO(b/240727590): Prefix with AREA_ 297 * 298 * TODO(b/239833099): Add a STATUS_ACTIVE option to let apps know if a feature is currently 299 * enabled. 300 * 301 * @param consumer {@link Consumer} interested in receiving updates to the status of 302 * rear display presentation mode. 303 */ 304 @Override addRearDisplayPresentationStatusListener( @onNull Consumer<ExtensionWindowAreaStatus> consumer)305 public void addRearDisplayPresentationStatusListener( 306 @NonNull Consumer<ExtensionWindowAreaStatus> consumer) { 307 synchronized (mLock) { 308 mRearDisplayPresentationStatusListeners.add(consumer); 309 310 // If current device state is still invalid, the initial value has not been provided 311 if (mCurrentDeviceState == INVALID_DEVICE_STATE) { 312 return; 313 } 314 @WindowAreaStatus int currentStatus = getCurrentRearDisplayPresentationModeStatus(); 315 DisplayMetrics metrics = currentStatus == STATUS_UNSUPPORTED ? new DisplayMetrics() 316 : getRearDisplayMetrics(); 317 consumer.accept( 318 new RearDisplayPresentationStatus(currentStatus, metrics)); 319 } 320 } 321 322 /** 323 * Removes a listener no longer interested in receiving updates. 324 * @param consumer no longer interested in receiving updates to RearDisplayPresentationStatus 325 */ 326 @Override removeRearDisplayPresentationStatusListener( @onNull Consumer<ExtensionWindowAreaStatus> consumer)327 public void removeRearDisplayPresentationStatusListener( 328 @NonNull Consumer<ExtensionWindowAreaStatus> consumer) { 329 synchronized (mLock) { 330 mRearDisplayPresentationStatusListeners.remove(consumer); 331 } 332 } 333 334 /** 335 * Creates and starts a rear display presentation session and sends state updates to the 336 * consumer provided. This consumer will receive a constant represented by 337 * {@link WindowAreaSessionState} to represent the state of the current rear display 338 * session. It will be translated to a more friendly interface in the library. 339 * 340 * Because this is being called from the OEM provided extensions, the library 341 * will post the result of the listener on the executor provided by the developer. 342 * 343 * Rear display presentation mode refers to a feature where an {@link Activity} can present 344 * additional content on a device with a second display that is facing the same direction 345 * as the rear camera (i.e. the cover display on a fold-in style device). The calling 346 * {@link Activity} stays on the user-facing display. 347 * 348 * @param activity that the OEM implementation will use as a base 349 * context and to identify the source display area of the request. 350 * The reference to the activity instance must not be stored in the OEM 351 * implementation to prevent memory leaks. 352 * @param consumer to provide updates to the client on the status of the session 353 * @throws UnsupportedOperationException if this method is called when rear display presentation 354 * mode is not available. This could be to an incompatible device state or when 355 * another process is currently in this mode. 356 */ 357 @Override startRearDisplayPresentationSession(@onNull Activity activity, @NonNull Consumer<@WindowAreaSessionState Integer> consumer)358 public void startRearDisplayPresentationSession(@NonNull Activity activity, 359 @NonNull Consumer<@WindowAreaSessionState Integer> consumer) { 360 synchronized (mLock) { 361 if (mRearDisplayPresentationController != null) { 362 // Rear display presentation session is already active 363 throw new IllegalStateException( 364 "Unable to start new rear display presentation session as one is already " 365 + "active"); 366 } 367 if (getCurrentRearDisplayPresentationModeStatus() 368 != WindowAreaComponent.STATUS_AVAILABLE) { 369 throw new IllegalStateException( 370 "Unable to start new rear display presentation session as the feature is " 371 + "is not currently available"); 372 } 373 374 mRearDisplayPresentationController = new RearDisplayPresentationController(activity, 375 stateStatus -> { 376 synchronized (mLock) { 377 if (stateStatus == SESSION_STATE_INACTIVE) { 378 // If the last reported session status was VISIBLE 379 // then the ACTIVE state should be dispatched before INACTIVE 380 // due to not having a good mechanism to know when 381 // the content is no longer visible before it's fully removed 382 if (getLastReportedRearDisplayPresentationStatus() 383 == SESSION_STATE_CONTENT_VISIBLE) { 384 consumer.accept(SESSION_STATE_ACTIVE); 385 } 386 mRearDisplayPresentationController = null; 387 } 388 mLastReportedRearDisplayPresentationStatus = stateStatus; 389 consumer.accept(stateStatus); 390 } 391 }); 392 RearDisplayPresentationRequestCallback deviceStateCallback = 393 new RearDisplayPresentationRequestCallback(activity, 394 mRearDisplayPresentationController); 395 DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder( 396 mConcurrentDisplayState).build(); 397 398 try { 399 mDeviceStateManager.requestState( 400 concurrentDisplayStateRequest, 401 mExecutor, 402 deviceStateCallback 403 ); 404 } catch (SecurityException e) { 405 // If a SecurityException occurs when invoking DeviceStateManager#requestState 406 // (e.g. if the caller is not in the foreground, or if it does not have the required 407 // permissions), we should first clean up our local state before re-throwing the 408 // SecurityException to the caller. Otherwise, subsequent attempts to 409 // startRearDisplayPresentationSession will always fail. 410 mRearDisplayPresentationController = null; 411 throw e; 412 } 413 } 414 } 415 416 /** 417 * Ends the current rear display presentation session and provides updates to the 418 * callback provided. When this is ended, the presented content from the calling 419 * {@link Activity} will also be removed from the rear facing display. 420 * Because this is being called from the OEM provided extensions, the result of the listener 421 * will be posted on the executor provided by the developer at the initial call site. 422 * 423 * Cancelling the {@link DeviceStateRequest} and exiting the rear display presentation state, 424 * will remove the presentation window from the cover display as the cover display is no longer 425 * enabled. 426 */ 427 @Override endRearDisplayPresentationSession()428 public void endRearDisplayPresentationSession() { 429 synchronized (mLock) { 430 if (mRearDisplayPresentationController != null) { 431 mDeviceStateManager.cancelStateRequest(); 432 } 433 } 434 } 435 436 @Nullable 437 @Override getRearDisplayPresentation()438 public ExtensionWindowAreaPresentation getRearDisplayPresentation() { 439 synchronized (mLock) { 440 ExtensionWindowAreaPresentation presentation = null; 441 if (mRearDisplayPresentationController != null) { 442 presentation = mRearDisplayPresentationController.getWindowAreaPresentation(); 443 } 444 return presentation; 445 } 446 } 447 448 @Override onSupportedStatesChanged(int[] supportedStates)449 public void onSupportedStatesChanged(int[] supportedStates) { 450 synchronized (mLock) { 451 mCurrentSupportedDeviceStates = supportedStates; 452 updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); 453 updateRearDisplayPresentationStatusListeners( 454 getCurrentRearDisplayPresentationModeStatus()); 455 } 456 } 457 458 @Override onStateChanged(int state)459 public void onStateChanged(int state) { 460 synchronized (mLock) { 461 mCurrentDeviceState = state; 462 updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); 463 updateRearDisplayPresentationStatusListeners( 464 getCurrentRearDisplayPresentationModeStatus()); 465 } 466 } 467 468 @GuardedBy("mLock") getCurrentRearDisplayModeStatus()469 private int getCurrentRearDisplayModeStatus() { 470 if (mRearDisplayState == INVALID_DEVICE_STATE) { 471 return WindowAreaComponent.STATUS_UNSUPPORTED; 472 } 473 474 if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)) { 475 return WindowAreaComponent.STATUS_UNAVAILABLE; 476 } 477 478 if (isRearDisplayActive()) { 479 return WindowAreaComponent.STATUS_ACTIVE; 480 } 481 482 return WindowAreaComponent.STATUS_AVAILABLE; 483 } 484 485 /** 486 * Helper method to determine if a rear display session is currently active by checking 487 * if the current device state is that which corresponds to {@code mRearDisplayState}. 488 * 489 * @return {@code true} if the device is in rear display state {@code false} if not 490 */ 491 @GuardedBy("mLock") isRearDisplayActive()492 private boolean isRearDisplayActive() { 493 return mCurrentDeviceState == mRearDisplayState; 494 } 495 496 @GuardedBy("mLock") updateRearDisplayStatusListeners(@indowAreaStatus int windowAreaStatus)497 private void updateRearDisplayStatusListeners(@WindowAreaStatus int windowAreaStatus) { 498 if (mRearDisplayState == INVALID_DEVICE_STATE) { 499 return; 500 } 501 synchronized (mLock) { 502 for (int i = 0; i < mRearDisplayStatusListeners.size(); i++) { 503 mRearDisplayStatusListeners.valueAt(i).accept(windowAreaStatus); 504 } 505 } 506 } 507 508 @GuardedBy("mLock") getCurrentRearDisplayPresentationModeStatus()509 private int getCurrentRearDisplayPresentationModeStatus() { 510 if (mConcurrentDisplayState == INVALID_DEVICE_STATE) { 511 return WindowAreaComponent.STATUS_UNSUPPORTED; 512 } 513 514 if (mCurrentDeviceState == mConcurrentDisplayState) { 515 return WindowAreaComponent.STATUS_ACTIVE; 516 } 517 518 if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState) 519 || isDeviceFolded()) { 520 return WindowAreaComponent.STATUS_UNAVAILABLE; 521 } 522 return WindowAreaComponent.STATUS_AVAILABLE; 523 } 524 525 @GuardedBy("mLock") isDeviceFolded()526 private boolean isDeviceFolded() { 527 return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState); 528 } 529 530 @GuardedBy("mLock") updateRearDisplayPresentationStatusListeners( @indowAreaStatus int windowAreaStatus)531 private void updateRearDisplayPresentationStatusListeners( 532 @WindowAreaStatus int windowAreaStatus) { 533 if (mConcurrentDisplayState == INVALID_DEVICE_STATE) { 534 return; 535 } 536 RearDisplayPresentationStatus consumerValue = new RearDisplayPresentationStatus( 537 windowAreaStatus, getRearDisplayMetrics()); 538 synchronized (mLock) { 539 for (int i = 0; i < mRearDisplayPresentationStatusListeners.size(); i++) { 540 mRearDisplayPresentationStatusListeners.valueAt(i).accept(consumerValue); 541 } 542 } 543 } 544 getRearDisplayAddress(Context context)545 private long getRearDisplayAddress(Context context) { 546 String address = context.getResources().getString( 547 R.string.config_rearDisplayPhysicalAddress); 548 return address.isEmpty() ? INVALID_DISPLAY_ADDRESS : Long.parseLong(address); 549 } 550 551 @GuardedBy("mLock") 552 @WindowAreaSessionState getLastReportedRearDisplayPresentationStatus()553 private int getLastReportedRearDisplayPresentationStatus() { 554 return mLastReportedRearDisplayPresentationStatus; 555 } 556 557 @VisibleForTesting rotateRearDisplayMetricsIfNeeded( @urface.Rotation int defaultDisplayRotation, @Surface.Rotation int rearDisplayRotation, @NonNull DisplayMetrics inOutMetrics)558 static void rotateRearDisplayMetricsIfNeeded( 559 @Surface.Rotation int defaultDisplayRotation, 560 @Surface.Rotation int rearDisplayRotation, 561 @NonNull DisplayMetrics inOutMetrics) { 562 // If the rear display has a non-zero rotation, it means the backing DisplayContent / 563 // DisplayRotation is fresh. 564 if (rearDisplayRotation != Surface.ROTATION_0) { 565 return; 566 } 567 568 // If the default display is 0 or 180, the rear display must also be 0 or 180. 569 if (defaultDisplayRotation == Surface.ROTATION_0 570 || defaultDisplayRotation == Surface.ROTATION_180) { 571 return; 572 } 573 574 final int heightPixels = inOutMetrics.heightPixels; 575 final int widthPixels = inOutMetrics.widthPixels; 576 inOutMetrics.widthPixels = heightPixels; 577 inOutMetrics.heightPixels = widthPixels; 578 579 final int noncompatHeightPixels = inOutMetrics.noncompatHeightPixels; 580 final int noncompatWidthPixels = inOutMetrics.noncompatWidthPixels; 581 inOutMetrics.noncompatWidthPixels = noncompatHeightPixels; 582 inOutMetrics.noncompatHeightPixels = noncompatWidthPixels; 583 } 584 585 /** 586 * Callback for the {@link DeviceStateRequest} to be notified of when the request has been 587 * activated or cancelled. This callback provides information to the client library 588 * on the status of the RearDisplay session through {@code mRearDisplaySessionCallback} 589 */ 590 private class RearDisplayStateRequestCallbackAdapter implements DeviceStateRequest.Callback { 591 592 private final Consumer<Integer> mRearDisplaySessionCallback; 593 RearDisplayStateRequestCallbackAdapter(@onNull Consumer<Integer> callback)594 RearDisplayStateRequestCallbackAdapter(@NonNull Consumer<Integer> callback) { 595 mRearDisplaySessionCallback = callback; 596 } 597 598 @Override onRequestActivated(@onNull DeviceStateRequest request)599 public void onRequestActivated(@NonNull DeviceStateRequest request) { 600 synchronized (mLock) { 601 if (request.equals(mRearDisplayStateRequest)) { 602 mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE; 603 mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus); 604 } 605 } 606 } 607 608 @Override onRequestCanceled(DeviceStateRequest request)609 public void onRequestCanceled(DeviceStateRequest request) { 610 synchronized (mLock) { 611 if (request.equals(mRearDisplayStateRequest)) { 612 mRearDisplayStateRequest = null; 613 } 614 mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE; 615 mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus); 616 } 617 } 618 } 619 } 620