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.policy;
18 
19 import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
20 import static android.os.PowerManager.WAKE_REASON_GESTURE;
21 import static android.os.PowerManager.WAKE_REASON_LID;
22 import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
23 import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
24 import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
25 import static android.view.KeyEvent.KEYCODE_POWER;
26 
27 import static com.android.server.policy.Flags.supportInputWakeupDelegate;
28 
29 import android.annotation.Nullable;
30 import android.content.Context;
31 import android.content.res.Resources;
32 import android.os.PowerManager;
33 import android.os.PowerManager.WakeReason;
34 import android.os.SystemClock;
35 import android.provider.Settings;
36 import android.util.Slog;
37 import android.view.Display;
38 import android.view.KeyEvent;
39 import android.view.WindowManager;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.internal.os.Clock;
43 import com.android.server.LocalServices;
44 
45 /** Policy controlling the decision and execution of window-related wake ups. */
46 class WindowWakeUpPolicy {
47     private static final String TAG = "WindowWakeUpPolicy";
48 
49     private static final boolean DEBUG = false;
50 
51     private final Context mContext;
52     private final PowerManager mPowerManager;
53     private final WindowManager mWindowManager;
54     private final Clock mClock;
55 
56     private final boolean mAllowTheaterModeWakeFromKey;
57     private final boolean mAllowTheaterModeWakeFromPowerKey;
58     private final boolean mAllowTheaterModeWakeFromMotion;
59     private final boolean mAllowTheaterModeWakeFromCameraLens;
60     private final boolean mAllowTheaterModeWakeFromLidSwitch;
61     private final boolean mAllowTheaterModeWakeFromWakeGesture;
62 
63     // The policy will handle input-based wake ups if this delegate is null.
64     @Nullable private WindowWakeUpPolicyInternal.InputWakeUpDelegate mInputWakeUpDelegate;
65 
WindowWakeUpPolicy(Context context)66     WindowWakeUpPolicy(Context context) {
67         this(context, Clock.SYSTEM_CLOCK);
68     }
69 
70     @VisibleForTesting
WindowWakeUpPolicy(Context context, Clock clock)71     WindowWakeUpPolicy(Context context, Clock clock) {
72         mContext = context;
73         mPowerManager = context.getSystemService(PowerManager.class);
74         mWindowManager = context.getSystemService(WindowManager.class);
75         mClock = clock;
76 
77         final Resources res = context.getResources();
78         mAllowTheaterModeWakeFromKey = res.getBoolean(
79                 com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
80         mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
81                 || res.getBoolean(
82                     com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
83         mAllowTheaterModeWakeFromMotion = res.getBoolean(
84                 com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
85         mAllowTheaterModeWakeFromCameraLens = res.getBoolean(
86                 com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
87         mAllowTheaterModeWakeFromLidSwitch = res.getBoolean(
88                 com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
89         mAllowTheaterModeWakeFromWakeGesture = res.getBoolean(
90                 com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
91         if (supportInputWakeupDelegate()) {
92             LocalServices.addService(WindowWakeUpPolicyInternal.class, new LocalService());
93         }
94     }
95 
96     private final class LocalService implements WindowWakeUpPolicyInternal {
97         @Override
setInputWakeUpDelegate(@ullable InputWakeUpDelegate delegate)98         public void setInputWakeUpDelegate(@Nullable InputWakeUpDelegate delegate) {
99             if (!supportInputWakeupDelegate()) {
100                 Slog.w(TAG, "Input wake up delegates not supported.");
101                 return;
102             }
103             mInputWakeUpDelegate = delegate;
104         }
105     }
106 
107     /**
108      * Wakes up from a key event.
109      *
110      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
111      * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
112      * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
113      * @return {@code true} if the policy allows the requested wake up and the request has been
114      *      executed; {@code false} otherwise.
115      */
wakeUpFromKey(long eventTime, int keyCode, boolean isDown)116     boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown) {
117         final boolean wakeAllowedDuringTheaterMode =
118                 keyCode == KEYCODE_POWER
119                         ? mAllowTheaterModeWakeFromPowerKey
120                         : mAllowTheaterModeWakeFromKey;
121         if (!canWakeUp(wakeAllowedDuringTheaterMode)) {
122             if (DEBUG) Slog.d(TAG, "Unable to wake up from " + KeyEvent.keyCodeToString(keyCode));
123             return false;
124         }
125         if (mInputWakeUpDelegate != null
126                 && mInputWakeUpDelegate.wakeUpFromKey(eventTime, keyCode, isDown)) {
127             return true;
128         }
129         wakeUp(
130                 eventTime,
131                 keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
132                 keyCode == KEYCODE_POWER ? "POWER" : "KEY");
133         return true;
134     }
135 
136     /**
137      * Wakes up from a motion event.
138      *
139      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
140      * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
141      * @return {@code true} if the policy allows the requested wake up and the request has been
142      *      executed; {@code false} otherwise.
143      */
wakeUpFromMotion(long eventTime, int source, boolean isDown)144     boolean wakeUpFromMotion(long eventTime, int source, boolean isDown) {
145         if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
146             if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
147             return false;
148         }
149         if (mInputWakeUpDelegate != null
150                 && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
151             return true;
152         }
153         wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION");
154         return true;
155     }
156 
157     /**
158      * Wakes up due to an opened camera cover.
159      *
160      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
161      * @return {@code true} if the policy allows the requested wake up and the request has been
162      *      executed; {@code false} otherwise.
163      */
wakeUpFromCameraCover(long eventTime)164     boolean wakeUpFromCameraCover(long eventTime) {
165         if (!canWakeUp(mAllowTheaterModeWakeFromCameraLens)) {
166             if (DEBUG) Slog.d(TAG, "Unable to wake up from camera cover.");
167             return false;
168         }
169         wakeUp(eventTime, WAKE_REASON_CAMERA_LAUNCH, "CAMERA_COVER");
170         return true;
171     }
172 
173     /**
174      * Wakes up due to an opened lid.
175      *
176      * @return {@code true} if the policy allows the requested wake up and the request has been
177      *      executed; {@code false} otherwise.
178      */
wakeUpFromLid()179     boolean wakeUpFromLid() {
180         if (!canWakeUp(mAllowTheaterModeWakeFromLidSwitch)) {
181             if (DEBUG) Slog.d(TAG, "Unable to wake up from lid.");
182             return false;
183         }
184         wakeUp(mClock.uptimeMillis(), WAKE_REASON_LID, "LID");
185         return true;
186     }
187 
188     /**
189      * Wakes up to prevent sleeping when opening camera through power button.
190      *
191      * @return {@code true} if the policy allows the requested wake up and the request has been
192      *      executed; {@code false} otherwise.
193      */
wakeUpFromPowerKeyCameraGesture()194     boolean wakeUpFromPowerKeyCameraGesture() {
195         if (!canWakeUp(mAllowTheaterModeWakeFromPowerKey)) {
196             if (DEBUG) Slog.d(TAG, "Unable to wake up from power key camera gesture.");
197             return false;
198         }
199         wakeUp(mClock.uptimeMillis(), WAKE_REASON_CAMERA_LAUNCH, "CAMERA_GESTURE_PREVENT_LOCK");
200         return true;
201     }
202 
203     /**
204      * Wake up from a wake gesture.
205      *
206      * @return {@code true} if the policy allows the requested wake up and the request has been
207      *      executed; {@code false} otherwise.
208      */
wakeUpFromWakeGesture()209     boolean wakeUpFromWakeGesture() {
210         if (!canWakeUp(mAllowTheaterModeWakeFromWakeGesture)) {
211             if (DEBUG) Slog.d(TAG, "Unable to wake up from gesture.");
212             return false;
213         }
214         wakeUp(mClock.uptimeMillis(), WAKE_REASON_GESTURE, "GESTURE");
215         return true;
216     }
217 
canWakeUp(boolean wakeInTheaterMode)218     private boolean canWakeUp(boolean wakeInTheaterMode) {
219         if (supportInputWakeupDelegate() && isDefaultDisplayOn()) {
220             // If the default display is on, theater mode should not influence whether or not
221             // waking up is allowed. This is because the theater mode checks are there to block
222             // the display from being on in situations where the user may not want it to be
223             // on (so if the display is already on, no need to check for theater mode at all).
224             return true;
225         }
226         final boolean isTheaterModeEnabled =
227                 Settings.Global.getInt(
228                         mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1;
229         return wakeInTheaterMode || !isTheaterModeEnabled;
230     }
231 
isDefaultDisplayOn()232     private boolean isDefaultDisplayOn() {
233         return Display.isOnState(mWindowManager.getDefaultDisplay().getState());
234     }
235 
236     /** Wakes up {@link PowerManager}. */
wakeUp(long wakeTime, @WakeReason int reason, String details)237     private void wakeUp(long wakeTime, @WakeReason int reason, String details) {
238         mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
239     }
240 }
241