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.ROTATION_UNDEFINED; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 22 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; 23 import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_LOCKED; 24 25 import android.annotation.Nullable; 26 import android.content.ActivityInfoProto; 27 import android.view.Surface; 28 29 import com.android.internal.protolog.common.ProtoLog; 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 = ROTATION_UNDEFINED; 55 56 private final boolean[] mSlots = new boolean[NUM_SLOTS]; 57 private final DisplayContent mDisplayContent; 58 DisplayRotationReversionController(DisplayContent content)59 DisplayRotationReversionController(DisplayContent content) { 60 mDisplayContent = content; 61 } 62 isRotationReversionEnabled()63 boolean isRotationReversionEnabled() { 64 return mDisplayContent.mDisplayRotationCompatPolicy != null 65 || mDisplayContent.getDisplayRotation().mFoldController != null 66 || mDisplayContent.getIgnoreOrientationRequest(); 67 } 68 beforeOverrideApplied(int slotIndex)69 void beforeOverrideApplied(int slotIndex) { 70 if (mSlots[slotIndex]) return; 71 maybeSaveUserRotation(); 72 mSlots[slotIndex] = true; 73 } 74 isOverrideActive(int slotIndex)75 boolean isOverrideActive(int slotIndex) { 76 return mSlots[slotIndex]; 77 } 78 79 @Nullable getSlotsCopy()80 boolean[] getSlotsCopy() { 81 return isRotationReversionEnabled() ? mSlots.clone() : null; 82 } 83 updateForNoSensorOverride()84 void updateForNoSensorOverride() { 85 if (!mSlots[REVERSION_TYPE_NOSENSOR]) { 86 if (isTopFullscreenActivityNoSensor()) { 87 ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override detected"); 88 beforeOverrideApplied(REVERSION_TYPE_NOSENSOR); 89 } 90 } else { 91 if (!isTopFullscreenActivityNoSensor()) { 92 ProtoLog.v(WM_DEBUG_ORIENTATION, "NOSENSOR override is absent: reverting"); 93 revertOverride(REVERSION_TYPE_NOSENSOR); 94 } 95 } 96 } 97 isAnyOverrideActive()98 boolean isAnyOverrideActive() { 99 for (int i = 0; i < NUM_SLOTS; ++i) { 100 if (mSlots[i]) { 101 return true; 102 } 103 } 104 return false; 105 } 106 revertOverride(int slotIndex)107 boolean revertOverride(int slotIndex) { 108 if (!mSlots[slotIndex]) return false; 109 mSlots[slotIndex] = false; 110 if (isAnyOverrideActive()) { 111 ProtoLog.v(WM_DEBUG_ORIENTATION, 112 "Other orientation overrides are in place: not reverting"); 113 return false; 114 } 115 // Only override if the rotation is frozen and there are no other active slots. 116 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 117 if (mUserRotationOverridden != ROTATION_UNDEFINED 118 && displayRotation.getUserRotationMode() == USER_ROTATION_LOCKED) { 119 displayRotation.setUserRotation(USER_ROTATION_LOCKED, mUserRotationOverridden, 120 /* caller= */ "DisplayRotationReversionController#revertOverride"); 121 mUserRotationOverridden = ROTATION_UNDEFINED; 122 return true; 123 } else { 124 return false; 125 } 126 } 127 maybeSaveUserRotation()128 private void maybeSaveUserRotation() { 129 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 130 if (!isAnyOverrideActive() 131 && displayRotation.getUserRotationMode() == USER_ROTATION_LOCKED) { 132 mUserRotationOverridden = displayRotation.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