1 /*
2  * Copyright (C) 2017 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.systemui.keyguard;
18 
19 import android.annotation.IntDef;
20 import android.app.IWallpaperManager;
21 import android.content.Context;
22 import android.content.res.Configuration;
23 import android.graphics.Point;
24 import android.os.Bundle;
25 import android.os.PowerManager;
26 import android.os.RemoteException;
27 import android.os.Trace;
28 import android.util.DisplayMetrics;
29 
30 import androidx.annotation.Nullable;
31 
32 import com.android.systemui.Dumpable;
33 import com.android.systemui.R;
34 import com.android.systemui.dagger.SysUISingleton;
35 import com.android.systemui.dump.DumpManager;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 
42 import javax.inject.Inject;
43 
44 /**
45  * Tracks the wakefulness lifecycle, including why we're waking or sleeping.
46  */
47 @SysUISingleton
48 public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
49         Dumpable {
50 
51     @IntDef(prefix = { "WAKEFULNESS_" }, value = {
52             WAKEFULNESS_ASLEEP,
53             WAKEFULNESS_WAKING,
54             WAKEFULNESS_AWAKE,
55             WAKEFULNESS_GOING_TO_SLEEP,
56     })
57     @Retention(RetentionPolicy.SOURCE)
58     public @interface Wakefulness {}
59 
60     public static final int WAKEFULNESS_ASLEEP = 0;
61     public static final int WAKEFULNESS_WAKING = 1;
62     public static final int WAKEFULNESS_AWAKE = 2;
63     public static final int WAKEFULNESS_GOING_TO_SLEEP = 3;
64 
65     private final Context mContext;
66     private final DisplayMetrics mDisplayMetrics;
67 
68     @Nullable
69     private final IWallpaperManager mWallpaperManagerService;
70 
71     private int mWakefulness = WAKEFULNESS_AWAKE;
72 
73     private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
74 
75     @Nullable
76     private Point mLastWakeOriginLocation = null;
77 
78     private @PowerManager.GoToSleepReason int mLastSleepReason =
79             PowerManager.GO_TO_SLEEP_REASON_MIN;
80 
81     @Nullable
82     private Point mLastSleepOriginLocation = null;
83 
84     @Inject
WakefulnessLifecycle( Context context, @Nullable IWallpaperManager wallpaperManagerService, DumpManager dumpManager)85     public WakefulnessLifecycle(
86             Context context,
87             @Nullable IWallpaperManager wallpaperManagerService,
88             DumpManager dumpManager) {
89         mContext = context;
90         mDisplayMetrics = context.getResources().getDisplayMetrics();
91         mWallpaperManagerService = wallpaperManagerService;
92 
93         dumpManager.registerDumpable(getClass().getSimpleName(), this);
94     }
95 
getWakefulness()96     public @Wakefulness int getWakefulness() {
97         return mWakefulness;
98     }
99 
100     /**
101      * Returns the most recent reason the device woke up. This is one of PowerManager.WAKE_REASON_*.
102      */
getLastWakeReason()103     public @PowerManager.WakeReason int getLastWakeReason() {
104         return mLastWakeReason;
105     }
106 
107     /**
108      * Returns the most recent reason the device went to sleep up. This is one of
109      * PowerManager.GO_TO_SLEEP_REASON_*.
110      */
getLastSleepReason()111     public @PowerManager.GoToSleepReason int getLastSleepReason() {
112         return mLastSleepReason;
113     }
114 
dispatchStartedWakingUp(@owerManager.WakeReason int pmWakeReason)115     public void dispatchStartedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
116         if (getWakefulness() == WAKEFULNESS_WAKING) {
117             return;
118         }
119         setWakefulness(WAKEFULNESS_WAKING);
120         mLastWakeReason = pmWakeReason;
121         updateLastWakeOriginLocation();
122 
123         if (mWallpaperManagerService != null) {
124             try {
125                 mWallpaperManagerService.notifyWakingUp(
126                         mLastWakeOriginLocation.x, mLastWakeOriginLocation.y, new Bundle());
127             } catch (RemoteException e) {
128                 e.printStackTrace();
129             }
130         }
131 
132         dispatch(Observer::onStartedWakingUp);
133     }
134 
dispatchFinishedWakingUp()135     public void dispatchFinishedWakingUp() {
136         if (getWakefulness() == WAKEFULNESS_AWAKE) {
137             return;
138         }
139         setWakefulness(WAKEFULNESS_AWAKE);
140         dispatch(Observer::onFinishedWakingUp);
141         dispatch(Observer::onPostFinishedWakingUp);
142     }
143 
dispatchStartedGoingToSleep(@owerManager.GoToSleepReason int pmSleepReason)144     public void dispatchStartedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
145         if (getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP) {
146             return;
147         }
148         setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
149         mLastSleepReason = pmSleepReason;
150         updateLastSleepOriginLocation();
151 
152         if (mWallpaperManagerService != null) {
153             try {
154                 mWallpaperManagerService.notifyGoingToSleep(
155                         mLastSleepOriginLocation.x, mLastSleepOriginLocation.y, new Bundle());
156             } catch (RemoteException e) {
157                 e.printStackTrace();
158             }
159         }
160 
161         dispatch(Observer::onStartedGoingToSleep);
162     }
163 
dispatchFinishedGoingToSleep()164     public void dispatchFinishedGoingToSleep() {
165         if (getWakefulness() == WAKEFULNESS_ASLEEP) {
166             return;
167         }
168         setWakefulness(WAKEFULNESS_ASLEEP);
169         dispatch(Observer::onFinishedGoingToSleep);
170     }
171 
172     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)173     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
174         pw.println("WakefulnessLifecycle:");
175         pw.println("  mWakefulness=" + mWakefulness);
176     }
177 
setWakefulness(@akefulness int wakefulness)178     private void setWakefulness(@Wakefulness int wakefulness) {
179         mWakefulness = wakefulness;
180         Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
181     }
182 
updateLastWakeOriginLocation()183     private void updateLastWakeOriginLocation() {
184         mLastWakeOriginLocation = null;
185 
186         switch (mLastWakeReason) {
187             case PowerManager.WAKE_REASON_POWER_BUTTON:
188                 mLastWakeOriginLocation = getPowerButtonOrigin();
189                 break;
190             default:
191                 mLastWakeOriginLocation = getDefaultWakeSleepOrigin();
192                 break;
193         }
194     }
195 
updateLastSleepOriginLocation()196     private void updateLastSleepOriginLocation() {
197         mLastSleepOriginLocation = null;
198 
199         switch (mLastSleepReason) {
200             case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
201                 mLastSleepOriginLocation = getPowerButtonOrigin();
202                 break;
203             default:
204                 mLastSleepOriginLocation = getDefaultWakeSleepOrigin();
205                 break;
206         }
207     }
208 
209     /**
210      * Returns the point on the screen closest to the physical power button.
211      */
getPowerButtonOrigin()212     private Point getPowerButtonOrigin() {
213         final boolean isPortrait = mContext.getResources().getConfiguration().orientation
214                 == Configuration.ORIENTATION_PORTRAIT;
215 
216         if (isPortrait) {
217             return new Point(
218                     mDisplayMetrics.widthPixels,
219                     mContext.getResources().getDimensionPixelSize(
220                             R.dimen.physical_power_button_center_screen_location_y));
221         } else {
222             return new Point(
223                     mContext.getResources().getDimensionPixelSize(
224                             R.dimen.physical_power_button_center_screen_location_y),
225                     mDisplayMetrics.heightPixels);
226         }
227     }
228 
229     /**
230      * Returns the point on the screen used as the default origin for wake/sleep events. This is the
231      * middle-bottom of the screen.
232      */
getDefaultWakeSleepOrigin()233     private Point getDefaultWakeSleepOrigin() {
234         return new Point(mDisplayMetrics.widthPixels / 2, mDisplayMetrics.heightPixels);
235     }
236 
237     public interface Observer {
onStartedWakingUp()238         default void onStartedWakingUp() {}
onFinishedWakingUp()239         default void onFinishedWakingUp() {}
240 
241         /**
242          * Called after the finished waking up call, ensuring it's after all the other listeners,
243          * reacting to {@link #onFinishedWakingUp()}
244          */
onPostFinishedWakingUp()245         default void onPostFinishedWakingUp() {}
onStartedGoingToSleep()246         default void onStartedGoingToSleep() {}
onFinishedGoingToSleep()247         default void onFinishedGoingToSleep() {}
248     }
249 }
250