OSDN Git Service

AOD: Prewarm display while waiting for brightness sensor
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / doze / DozeTriggers.java
1 /*
2  * Copyright (C) 2016 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.doze;
18
19 import android.app.AlarmManager;
20 import android.app.UiModeManager;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.res.Configuration;
26 import android.hardware.Sensor;
27 import android.hardware.SensorEvent;
28 import android.hardware.SensorEventListener;
29 import android.hardware.SensorManager;
30 import android.os.Handler;
31 import android.os.SystemClock;
32 import android.os.UserHandle;
33 import android.text.format.Formatter;
34 import android.util.Log;
35
36 import com.android.internal.hardware.AmbientDisplayConfiguration;
37 import com.android.internal.util.Preconditions;
38 import com.android.systemui.statusbar.phone.DozeParameters;
39 import com.android.systemui.util.Assert;
40 import com.android.systemui.util.wakelock.WakeLock;
41
42 import java.io.PrintWriter;
43 import java.util.function.IntConsumer;
44
45 /**
46  * Handles triggers for ambient state changes.
47  */
48 public class DozeTriggers implements DozeMachine.Part {
49
50     private static final String TAG = "DozeTriggers";
51     private static final boolean DEBUG = DozeService.DEBUG;
52
53     /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
54     private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
55
56     private final Context mContext;
57     private final DozeMachine mMachine;
58     private final DozeSensors mDozeSensors;
59     private final DozeHost mDozeHost;
60     private final AmbientDisplayConfiguration mConfig;
61     private final DozeParameters mDozeParameters;
62     private final SensorManager mSensorManager;
63     private final Handler mHandler;
64     private final WakeLock mWakeLock;
65     private final boolean mAllowPulseTriggers;
66     private final UiModeManager mUiModeManager;
67     private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
68
69     private long mNotificationPulseTime;
70     private boolean mPulsePending;
71
72
73     public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
74             AlarmManager alarmManager, AmbientDisplayConfiguration config,
75             DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
76             WakeLock wakeLock, boolean allowPulseTriggers) {
77         mContext = context;
78         mMachine = machine;
79         mDozeHost = dozeHost;
80         mConfig = config;
81         mDozeParameters = dozeParameters;
82         mSensorManager = sensorManager;
83         mHandler = handler;
84         mWakeLock = wakeLock;
85         mAllowPulseTriggers = allowPulseTriggers;
86         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
87                 config, wakeLock, this::onSensor, this::onProximityFar,
88                 new AlwaysOnDisplayPolicy(context));
89         mUiModeManager = mContext.getSystemService(UiModeManager.class);
90     }
91
92     private void onNotification() {
93         if (DozeMachine.DEBUG) Log.d(TAG, "requestNotificationPulse");
94         mNotificationPulseTime = SystemClock.elapsedRealtime();
95         if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) return;
96         requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
97         DozeLog.traceNotificationPulse(mContext);
98     }
99
100     private void onWhisper() {
101         requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
102     }
103
104     private void proximityCheckThenCall(IntConsumer callback,
105             boolean alreadyPerformedProxCheck,
106             int pulseReason) {
107         Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
108         if (alreadyPerformedProxCheck) {
109             callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
110         } else if (cachedProxFar != null) {
111             callback.accept(cachedProxFar ? ProximityCheck.RESULT_FAR : ProximityCheck.RESULT_NEAR);
112         } else {
113             final long start = SystemClock.uptimeMillis();
114             new ProximityCheck() {
115                 @Override
116                 public void onProximityResult(int result) {
117                     final long end = SystemClock.uptimeMillis();
118                     DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
119                             end - start, pulseReason);
120                     callback.accept(result);
121                 }
122             }.check();
123         }
124     }
125
126     private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
127             float screenX, float screenY) {
128         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
129         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
130         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
131
132         if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !isLongPress) {
133             proximityCheckThenCall((result) -> {
134                 if (result == ProximityCheck.RESULT_NEAR) {
135                     // In pocket, drop event.
136                     return;
137                 }
138                 if (isDoubleTap) {
139                     mDozeHost.onDoubleTap(screenX, screenY);
140                     mMachine.wakeUp();
141                 } else {
142                     mDozeHost.extendPulse();
143                 }
144             }, sensorPerformedProxCheck, pulseReason);
145             return;
146         } else {
147             requestPulse(pulseReason, sensorPerformedProxCheck);
148         }
149
150         if (isPickup) {
151             final long timeSinceNotification =
152                     SystemClock.elapsedRealtime() - mNotificationPulseTime;
153             final boolean withinVibrationThreshold =
154                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
155             DozeLog.tracePickupPulse(mContext, withinVibrationThreshold);
156         }
157     }
158
159     private void onProximityFar(boolean far) {
160         final boolean near = !far;
161         final DozeMachine.State state = mMachine.getState();
162         final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
163         final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
164         final boolean aod = (state == DozeMachine.State.DOZE_AOD);
165
166         if (state == DozeMachine.State.DOZE_PULSING) {
167             boolean ignoreTouch = near;
168             if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
169             mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
170         }
171         if (far && (paused || pausing)) {
172             if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
173             mMachine.requestState(DozeMachine.State.DOZE_AOD);
174         } else if (near && aod) {
175             if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD");
176             mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
177         }
178     }
179
180     @Override
181     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
182         switch (newState) {
183             case INITIALIZED:
184                 mBroadcastReceiver.register(mContext);
185                 mDozeHost.addCallback(mHostCallback);
186                 checkTriggersAtInit();
187                 break;
188             case DOZE:
189             case DOZE_AOD:
190                 mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
191                 if (oldState != DozeMachine.State.INITIALIZED) {
192                     mDozeSensors.reregisterAllSensors();
193                 }
194                 mDozeSensors.setListening(true);
195                 break;
196             case DOZE_AOD_PAUSED:
197             case DOZE_AOD_PAUSING:
198                 mDozeSensors.setProxListening(true);
199                 mDozeSensors.setListening(false);
200                 break;
201             case DOZE_PULSING:
202                 mDozeSensors.setTouchscreenSensorsListening(false);
203                 mDozeSensors.setProxListening(true);
204                 break;
205             case FINISH:
206                 mBroadcastReceiver.unregister(mContext);
207                 mDozeHost.removeCallback(mHostCallback);
208                 mDozeSensors.setListening(false);
209                 mDozeSensors.setProxListening(false);
210                 break;
211             default:
212         }
213     }
214
215     private void checkTriggersAtInit() {
216         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
217                 || mDozeHost.isPowerSaveActive()
218                 || mDozeHost.isBlockingDoze()
219                 || !mDozeHost.isProvisioned()) {
220             mMachine.requestState(DozeMachine.State.FINISH);
221         }
222     }
223
224     private void requestPulse(final int reason, boolean performedProxCheck) {
225         Assert.isMainThread();
226         mDozeHost.extendPulse();
227         if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
228             if (mAllowPulseTriggers) {
229                 DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
230                         mDozeHost.isPulsingBlocked());
231             }
232             return;
233         }
234
235         mPulsePending = true;
236         proximityCheckThenCall((result) -> {
237             if (result == ProximityCheck.RESULT_NEAR) {
238                 // in pocket, abort pulse
239                 mPulsePending = false;
240             } else {
241                 // not in pocket, continue pulsing
242                 continuePulseRequest(reason);
243             }
244         }, !mDozeParameters.getProxCheckBeforePulse() || performedProxCheck, reason);
245     }
246
247     private boolean canPulse() {
248         return mMachine.getState() == DozeMachine.State.DOZE
249                 || mMachine.getState() == DozeMachine.State.DOZE_AOD;
250     }
251
252     private void continuePulseRequest(int reason) {
253         mPulsePending = false;
254         if (mDozeHost.isPulsingBlocked() || !canPulse()) {
255             DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
256                     mDozeHost.isPulsingBlocked());
257             return;
258         }
259         mMachine.requestPulse(reason);
260     }
261
262     @Override
263     public void dump(PrintWriter pw) {
264         pw.print(" notificationPulseTime=");
265         pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
266
267         pw.print(" pulsePending="); pw.println(mPulsePending);
268         pw.println("DozeSensors:");
269         mDozeSensors.dump(pw);
270     }
271
272     private abstract class ProximityCheck implements SensorEventListener, Runnable {
273         private static final int TIMEOUT_DELAY_MS = 500;
274
275         protected static final int RESULT_UNKNOWN = 0;
276         protected static final int RESULT_NEAR = 1;
277         protected static final int RESULT_FAR = 2;
278         protected static final int RESULT_NOT_CHECKED = 3;
279
280         private boolean mRegistered;
281         private boolean mFinished;
282         private float mMaxRange;
283
284         protected abstract void onProximityResult(int result);
285
286         public void check() {
287             Preconditions.checkState(!mFinished && !mRegistered);
288             final Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
289             if (sensor == null) {
290                 if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
291                 finishWithResult(RESULT_UNKNOWN);
292                 return;
293             }
294             mDozeSensors.setDisableSensorsInterferingWithProximity(true);
295
296             mMaxRange = sensor.getMaximumRange();
297             mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
298                     mHandler);
299             mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
300             mWakeLock.acquire();
301             mRegistered = true;
302         }
303
304         @Override
305         public void onSensorChanged(SensorEvent event) {
306             if (event.values.length == 0) {
307                 if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
308                 finishWithResult(RESULT_UNKNOWN);
309             } else {
310                 if (DozeMachine.DEBUG) {
311                     Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
312                 }
313                 final boolean isNear = event.values[0] < mMaxRange;
314                 finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
315             }
316         }
317
318         @Override
319         public void run() {
320             if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
321             finishWithResult(RESULT_UNKNOWN);
322         }
323
324         private void finishWithResult(int result) {
325             if (mFinished) return;
326             boolean wasRegistered = mRegistered;
327             if (mRegistered) {
328                 mHandler.removeCallbacks(this);
329                 mSensorManager.unregisterListener(this);
330                 mDozeSensors.setDisableSensorsInterferingWithProximity(false);
331                 mRegistered = false;
332             }
333             onProximityResult(result);
334             if (wasRegistered) {
335                 mWakeLock.release();
336             }
337             mFinished = true;
338         }
339
340         @Override
341         public void onAccuracyChanged(Sensor sensor, int accuracy) {
342             // noop
343         }
344     }
345
346     private class TriggerReceiver extends BroadcastReceiver {
347         private boolean mRegistered;
348
349         @Override
350         public void onReceive(Context context, Intent intent) {
351             if (PULSE_ACTION.equals(intent.getAction())) {
352                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
353                 requestPulse(DozeLog.PULSE_REASON_INTENT, false /* performedProxCheck */);
354             }
355             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
356                 mMachine.requestState(DozeMachine.State.FINISH);
357             }
358             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
359                 mDozeSensors.onUserSwitched();
360             }
361         }
362
363         public void register(Context context) {
364             if (mRegistered) {
365                 return;
366             }
367             IntentFilter filter = new IntentFilter(PULSE_ACTION);
368             filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
369             filter.addAction(Intent.ACTION_USER_SWITCHED);
370             context.registerReceiver(this, filter);
371             mRegistered = true;
372         }
373
374         public void unregister(Context context) {
375             if (!mRegistered) {
376                 return;
377             }
378             context.unregisterReceiver(this);
379             mRegistered = false;
380         }
381     }
382
383     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
384         @Override
385         public void onNotificationHeadsUp() {
386             onNotification();
387         }
388
389         @Override
390         public void onPowerSaveChanged(boolean active) {
391             if (active) {
392                 mMachine.requestState(DozeMachine.State.FINISH);
393             }
394         }
395     };
396 }