1 /* 2 * Copyright (C) 2023 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 com.android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 21 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; 22 23 import android.annotation.Nullable; 24 import android.app.WindowConfiguration; 25 import android.content.ActivityInfoProto; 26 import android.view.Surface; 27 28 import com.android.internal.protolog.common.ProtoLog; 29 import com.android.server.policy.WindowManagerPolicy; 30 31 /** 32 * Defines the behavior of reversion from device rotation overrides. 33 * 34 * <p>There are 3 override types: 35 * <ol> 36 * <li>The top application has {@link SCREEN_ORIENTATION_NOSENSOR} set and is rotated to 37 * {@link ROTATION_0}. 38 * <li>Camera compat treatment has rotated the app {@link DisplayRotationCompatPolicy}. 39 * <li>The device is half-folded and has auto-rotate is temporarily enabled. 40 * </ol> 41 * 42 * <p>Before an override is enabled, a component should call {@code beforeOverrideApplied}. When 43 * it wishes to revert, it should call {@code revertOverride}. The user rotation will be restored 44 * if there are no other overrides present. 45 */ 46 final class DisplayRotationReversionController { 47 48 static final int REVERSION_TYPE_NOSENSOR = 0; 49 static final int REVERSION_TYPE_CAMERA_COMPAT = 1; 50 static final int REVERSION_TYPE_HALF_FOLD = 2; 51 private static final int NUM_SLOTS = 3; 52 53 @Surface.Rotation 54 private int mUserRotationOverridden = WindowConfiguration.ROTATION_UNDEFINED; 55 @WindowManagerPolicy.UserRotationMode 56 private int mUserRotationModeOverridden; 57 58 private final boolean[] mSlots = new boolean[NUM_SLOTS]; 59 private final DisplayContent mDisplayContent; 60 DisplayRotationReversionController(DisplayContent content)61 DisplayRotationReversionController(DisplayContent content) { 62 mDisplayContent = content; 63 } 64 isRotationReversionEnabled()65 boolean isRotationReversionEnabled() { 66 return mDisplayContent.mDisplayRotationCompatPolicy != null 67 || mDisplayContent.getDisplayRotation().mFoldController != null 68 || mDisplayContent.getIgnoreOrientationRequest(); 69 } 70 beforeOverrideApplied(int slotIndex)71 void beforeOverrideApplied(int slotIndex) { 72 if (mSlots[slotIndex]) return; 73 maybeSaveUserRotation(); 74 mSlots[slotIndex] = true; 75 } 76 isOverrideActive(int slotIndex)77 boolean isOverrideActive(int slotIndex) { 78 return mSlots[slotIndex]; 79 } 80 81 @Nullable getSlotsCopy()82 boolean[] getSlotsCopy() { 83 return isRotationReversionEnabled() ? mSlots.clone() : null; 84 } 85 updateForNoSensorOverride()86 void updateForNoSensorOverride() { 87 if (!mSlots[REVERSION_TYPE_NOSENSOR]) { 88 if (isTopFullscreenActivityNoSensor()) { 89 ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override detected"); 90 beforeOverrideApplied(REVERSION_TYPE_NOSENSOR); 91 } 92 } else { 93 if (!isTopFullscreenActivityNoSensor()) { 94 ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override is absent: reverting"); 95 revertOverride(REVERSION_TYPE_NOSENSOR); 96 } 97 } 98 } 99 isAnyOverrideActive()100 boolean isAnyOverrideActive() { 101 for (int i = 0; i < NUM_SLOTS; ++i) { 102 if (mSlots[i]) { 103 return true; 104 } 105 } 106 return false; 107 } 108 revertOverride(int slotIndex)109 boolean revertOverride(int slotIndex) { 110 if (!mSlots[slotIndex]) return false; 111 mSlots[slotIndex] = false; 112 if (isAnyOverrideActive()) { 113 ProtoLog.v(WM_DEBUG_ORIENTATION, 114 "Other orientation overrides are in place: not reverting"); 115 return false; 116 } 117 // Only override if the rotation is frozen and there are no other active slots. 118 if (mDisplayContent.getDisplayRotation().isRotationFrozen()) { 119 mDisplayContent.getDisplayRotation().setUserRotation( 120 mUserRotationModeOverridden, 121 mUserRotationOverridden); 122 return true; 123 } else { 124 return false; 125 } 126 } 127 maybeSaveUserRotation()128 private void maybeSaveUserRotation() { 129 if (!isAnyOverrideActive()) { 130 mUserRotationModeOverridden = 131 mDisplayContent.getDisplayRotation().getUserRotationMode(); 132 mUserRotationOverridden = mDisplayContent.getDisplayRotation().getUserRotation(); 133 } 134 } 135 isTopFullscreenActivityNoSensor()136 private boolean isTopFullscreenActivityNoSensor() { 137 final Task topFullscreenTask = 138 mDisplayContent.getTask( 139 t -> t.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); 140 if (topFullscreenTask != null) { 141 final ActivityRecord topActivity = 142 topFullscreenTask.topRunningActivity(); 143 return topActivity != null && topActivity.getOrientation() 144 == ActivityInfoProto.SCREEN_ORIENTATION_NOSENSOR; 145 } 146 return false; 147 } 148 } 149