OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev am: 732a127636
[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.annotation.Nullable;
20 import android.app.AlarmManager;
21 import android.app.UiModeManager;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.res.Configuration;
27 import android.hardware.Sensor;
28 import android.hardware.SensorEvent;
29 import android.hardware.SensorEventListener;
30 import android.hardware.SensorManager;
31 import android.hardware.display.AmbientDisplayConfiguration;
32 import android.metrics.LogMaker;
33 import android.os.Handler;
34 import android.os.SystemClock;
35 import android.os.UserHandle;
36 import android.text.format.Formatter;
37 import android.util.Log;
38
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.logging.MetricsLogger;
41 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
42 import com.android.internal.util.Preconditions;
43 import com.android.systemui.Dependency;
44 import com.android.systemui.R;
45 import com.android.systemui.dock.DockManager;
46 import com.android.systemui.statusbar.phone.DozeParameters;
47 import com.android.systemui.util.Assert;
48 import com.android.systemui.util.wakelock.WakeLock;
49
50 import java.io.PrintWriter;
51 import java.util.function.IntConsumer;
52
53 /**
54  * Handles triggers for ambient state changes.
55  */
56 public class DozeTriggers implements DozeMachine.Part {
57
58     private static final String TAG = "DozeTriggers";
59     private static final boolean DEBUG = DozeService.DEBUG;
60
61     /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
62     private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
63
64     /**
65      * Last value sent by the wake-display sensor.
66      * Assuming that the screen should start on.
67      */
68     private static boolean sWakeDisplaySensorState = true;
69
70     private final Context mContext;
71     private final DozeMachine mMachine;
72     private final DozeSensors mDozeSensors;
73     private final DozeHost mDozeHost;
74     private final AmbientDisplayConfiguration mConfig;
75     private final DozeParameters mDozeParameters;
76     private final SensorManager mSensorManager;
77     private final Handler mHandler;
78     private final WakeLock mWakeLock;
79     private final boolean mAllowPulseTriggers;
80     private final UiModeManager mUiModeManager;
81     private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
82     private final DockEventListener mDockEventListener = new DockEventListener();
83     private final DockManager mDockManager;
84
85     private long mNotificationPulseTime;
86     private boolean mPulsePending;
87
88     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
89
90     public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
91             AlarmManager alarmManager, AmbientDisplayConfiguration config,
92             DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
93             WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
94         mContext = context;
95         mMachine = machine;
96         mDozeHost = dozeHost;
97         mConfig = config;
98         mDozeParameters = dozeParameters;
99         mSensorManager = sensorManager;
100         mHandler = handler;
101         mWakeLock = wakeLock;
102         mAllowPulseTriggers = allowPulseTriggers;
103         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
104                 config, wakeLock, this::onSensor, this::onProximityFar,
105                 dozeParameters.getPolicy());
106         mUiModeManager = mContext.getSystemService(UiModeManager.class);
107         mDockManager = dockManager;
108     }
109
110     private void onNotification(Runnable onPulseSuppressedListener) {
111         if (DozeMachine.DEBUG) {
112             Log.d(TAG, "requestNotificationPulse");
113         }
114         if (!sWakeDisplaySensorState) {
115             Log.d(TAG, "Wake display false. Pulse denied.");
116             runIfNotNull(onPulseSuppressedListener);
117             DozeLog.tracePulseDropped(mContext, "wakeDisplaySensor");
118             return;
119         }
120         mNotificationPulseTime = SystemClock.elapsedRealtime();
121         if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
122             runIfNotNull(onPulseSuppressedListener);
123             DozeLog.tracePulseDropped(mContext, "pulseOnNotificationsDisabled");
124             return;
125         }
126         requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
127                 onPulseSuppressedListener);
128         DozeLog.traceNotificationPulse(mContext);
129     }
130
131     private static void runIfNotNull(Runnable runnable) {
132         if (runnable != null) {
133             runnable.run();
134         }
135     }
136
137     private void proximityCheckThenCall(IntConsumer callback,
138             boolean alreadyPerformedProxCheck,
139             int reason) {
140         Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
141         if (alreadyPerformedProxCheck) {
142             callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
143         } else if (cachedProxFar != null) {
144             callback.accept(cachedProxFar ? ProximityCheck.RESULT_FAR : ProximityCheck.RESULT_NEAR);
145         } else {
146             final long start = SystemClock.uptimeMillis();
147             new ProximityCheck() {
148                 @Override
149                 public void onProximityResult(int result) {
150                     final long end = SystemClock.uptimeMillis();
151                     DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
152                             end - start, reason);
153                     callback.accept(result);
154                 }
155             }.check();
156         }
157     }
158
159     @VisibleForTesting
160     void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
161         boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
162         boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
163         boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
164         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
165         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
166         boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
167         boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
168
169         if (isWakeDisplay) {
170             onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
171         } else if (isLongPress) {
172             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
173                     null /* onPulseSupressedListener */);
174         } else if (isWakeLockScreen) {
175             if (wakeEvent) {
176                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
177                         null /* onPulseSupressedListener */);
178             }
179         } else {
180             proximityCheckThenCall((result) -> {
181                 if (result == ProximityCheck.RESULT_NEAR) {
182                     // In pocket, drop event.
183                     return;
184                 }
185                 if (isDoubleTap || isTap) {
186                     if (screenX != -1 && screenY != -1) {
187                         mDozeHost.onSlpiTap(screenX, screenY);
188                     }
189                     gentleWakeUp(pulseReason);
190                 } else if (isPickup) {
191                     gentleWakeUp(pulseReason);
192                 } else {
193                     mDozeHost.extendPulse(pulseReason);
194                 }
195             }, true /* alreadyPerformedProxCheck */, pulseReason);
196         }
197
198         if (isPickup) {
199             final long timeSinceNotification =
200                     SystemClock.elapsedRealtime() - mNotificationPulseTime;
201             final boolean withinVibrationThreshold =
202                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
203             DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
204         }
205     }
206
207     private void gentleWakeUp(int reason) {
208         // Log screen wake up reason (lift/pickup, tap, double-tap)
209         mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
210                 .setType(MetricsEvent.TYPE_UPDATE)
211                 .setSubtype(reason));
212         if (mDozeParameters.getDisplayNeedsBlanking()) {
213             // Let's prepare the display to wake-up by drawing black.
214             // This will cover the hardware wake-up sequence, where the display
215             // becomes black for a few frames.
216             mDozeHost.setAodDimmingScrim(1f);
217         }
218         mMachine.wakeUp();
219     }
220
221     private void onProximityFar(boolean far) {
222         // Proximity checks are asynchronous and the user might have interacted with the phone
223         // when a new event is arriving. This means that a state transition might have happened
224         // and the proximity check is now obsolete.
225         if (mMachine.isExecutingTransition()) {
226             Log.w(TAG, "onProximityFar called during transition. Ignoring sensor response.");
227             return;
228         }
229
230         final boolean near = !far;
231         final DozeMachine.State state = mMachine.getState();
232         final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
233         final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
234         final boolean aod = (state == DozeMachine.State.DOZE_AOD);
235
236         if (state == DozeMachine.State.DOZE_PULSING
237                 || state == DozeMachine.State.DOZE_PULSING_BRIGHT) {
238             boolean ignoreTouch = near;
239             if (DEBUG) {
240                 Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
241             }
242             mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
243         }
244
245         if (far && (paused || pausing)) {
246             if (DEBUG) {
247                 Log.i(TAG, "Prox FAR, unpausing AOD");
248             }
249             mMachine.requestState(DozeMachine.State.DOZE_AOD);
250         } else if (near && aod) {
251             if (DEBUG) {
252                 Log.i(TAG, "Prox NEAR, pausing AOD");
253             }
254             mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
255         }
256     }
257
258     /**
259      * When a wake screen event is received from a sensor
260      * @param wake {@code true} when it's time to wake up, {@code false} when we should sleep.
261      * @param state The current state, or null if the state could not be determined due to enqueued
262      *              transitions.
263      */
264     private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
265         DozeLog.traceWakeDisplay(wake);
266         sWakeDisplaySensorState = wake;
267
268         if (wake) {
269             proximityCheckThenCall((result) -> {
270                 if (result == ProximityCheck.RESULT_NEAR) {
271                     // In pocket, drop event.
272                     return;
273                 }
274                 if (state == DozeMachine.State.DOZE) {
275                     mMachine.requestState(DozeMachine.State.DOZE_AOD);
276                     // Logs AOD open due to sensor wake up.
277                     mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
278                             .setType(MetricsEvent.TYPE_OPEN)
279                             .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
280                 }
281             }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
282         } else {
283             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
284             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
285             if (!pausing && !paused) {
286                 mMachine.requestState(DozeMachine.State.DOZE);
287                 // Logs AOD close due to sensor wake up.
288                 mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
289                         .setType(MetricsEvent.TYPE_CLOSE)
290                         .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
291             }
292         }
293     }
294
295     @Override
296     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
297         switch (newState) {
298             case INITIALIZED:
299                 mBroadcastReceiver.register(mContext);
300                 mDozeHost.addCallback(mHostCallback);
301                 if (mDockManager != null) {
302                     mDockManager.addListener(mDockEventListener);
303                 }
304                 mDozeSensors.requestTemporaryDisable();
305                 checkTriggersAtInit();
306                 break;
307             case DOZE:
308             case DOZE_AOD:
309                 mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
310                 mDozeSensors.setListening(true);
311                 mDozeSensors.setPaused(false);
312                 if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) {
313                     onWakeScreen(false, newState);
314                 }
315                 break;
316             case DOZE_AOD_PAUSED:
317             case DOZE_AOD_PAUSING:
318                 mDozeSensors.setProxListening(true);
319                 mDozeSensors.setPaused(true);
320                 break;
321             case DOZE_PULSING:
322             case DOZE_PULSING_BRIGHT:
323                 mDozeSensors.setTouchscreenSensorsListening(false);
324                 mDozeSensors.setProxListening(true);
325                 mDozeSensors.setPaused(false);
326                 break;
327             case DOZE_PULSE_DONE:
328                 mDozeSensors.requestTemporaryDisable();
329                 // A pulse will temporarily disable sensors that require a touch screen.
330                 // Let's make sure that they are re-enabled when the pulse is over.
331                 mDozeSensors.updateListening();
332                 break;
333             case FINISH:
334                 mBroadcastReceiver.unregister(mContext);
335                 mDozeHost.removeCallback(mHostCallback);
336                 if (mDockManager != null) {
337                     mDockManager.removeListener(mDockEventListener);
338                 }
339                 mDozeSensors.setListening(false);
340                 mDozeSensors.setProxListening(false);
341                 break;
342             default:
343         }
344     }
345
346     private void checkTriggersAtInit() {
347         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
348                 || mDozeHost.isPowerSaveActive()
349                 || mDozeHost.isBlockingDoze()
350                 || !mDozeHost.isProvisioned()) {
351             mMachine.requestState(DozeMachine.State.FINISH);
352         }
353     }
354
355     private void requestPulse(final int reason, boolean performedProxCheck,
356             Runnable onPulseSuppressedListener) {
357         Assert.isMainThread();
358         mDozeHost.extendPulse(reason);
359
360         // When already pulsing we're allowed to show the wallpaper directly without
361         // requesting a new pulse.
362         if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
363                 && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
364             mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
365             return;
366         }
367
368         if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
369             if (mAllowPulseTriggers) {
370                 DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
371                         mDozeHost.isPulsingBlocked());
372             }
373             runIfNotNull(onPulseSuppressedListener);
374             return;
375         }
376
377         mPulsePending = true;
378         proximityCheckThenCall((result) -> {
379             if (result == ProximityCheck.RESULT_NEAR) {
380                 // in pocket, abort pulse
381                 DozeLog.tracePulseDropped(mContext, "inPocket");
382                 mPulsePending = false;
383                 runIfNotNull(onPulseSuppressedListener);
384             } else {
385                 // not in pocket, continue pulsing
386                 continuePulseRequest(reason);
387             }
388         }, !mDozeParameters.getProxCheckBeforePulse() || performedProxCheck, reason);
389
390         // Logs request pulse reason on AOD screen.
391         mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
392                 .setType(MetricsEvent.TYPE_UPDATE).setSubtype(reason));
393     }
394
395     private boolean canPulse() {
396         return mMachine.getState() == DozeMachine.State.DOZE
397                 || mMachine.getState() == DozeMachine.State.DOZE_AOD;
398     }
399
400     private void continuePulseRequest(int reason) {
401         mPulsePending = false;
402         if (mDozeHost.isPulsingBlocked() || !canPulse()) {
403             DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
404                     mDozeHost.isPulsingBlocked());
405             return;
406         }
407         mMachine.requestPulse(reason);
408     }
409
410     @Override
411     public void dump(PrintWriter pw) {
412         pw.print(" notificationPulseTime=");
413         pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
414
415         pw.print(" pulsePending="); pw.println(mPulsePending);
416         pw.println("DozeSensors:");
417         mDozeSensors.dump(pw);
418     }
419
420     /**
421      * @see DozeSensors.ProxSensor
422      */
423     private abstract class ProximityCheck implements SensorEventListener, Runnable {
424         private static final int TIMEOUT_DELAY_MS = 500;
425
426         protected static final int RESULT_UNKNOWN = 0;
427         protected static final int RESULT_NEAR = 1;
428         protected static final int RESULT_FAR = 2;
429         protected static final int RESULT_NOT_CHECKED = 3;
430
431         private boolean mRegistered;
432         private boolean mFinished;
433         private float mMaxRange;
434         private boolean mUsingBrightnessSensor;
435
436         protected abstract void onProximityResult(int result);
437
438         public void check() {
439             Preconditions.checkState(!mFinished && !mRegistered);
440             Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
441                     mContext.getString(R.string.doze_brightness_sensor_type));
442             mUsingBrightnessSensor = sensor != null;
443             if (sensor == null) {
444                 sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
445             }
446             if (sensor == null) {
447                 if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
448                 finishWithResult(RESULT_UNKNOWN);
449                 return;
450             }
451             mDozeSensors.setDisableSensorsInterferingWithProximity(true);
452
453             mMaxRange = sensor.getMaximumRange();
454             mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
455                     mHandler);
456             mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
457             mWakeLock.acquire(TAG);
458             mRegistered = true;
459         }
460
461         /**
462          * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
463          */
464         @Override
465         public void onSensorChanged(SensorEvent event) {
466             if (event.values.length == 0) {
467                 if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
468                 finishWithResult(RESULT_UNKNOWN);
469             } else {
470                 if (DozeMachine.DEBUG) {
471                     Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
472                 }
473                 final boolean isNear;
474                 if (mUsingBrightnessSensor) {
475                     // The custom brightness sensor is gated by the proximity sensor and will
476                     // return 0 whenever prox is covered.
477                     isNear = event.values[0] == 0;
478                 } else {
479                     isNear = event.values[0] < mMaxRange;
480                 }
481                 finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
482             }
483         }
484
485         @Override
486         public void run() {
487             if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
488             finishWithResult(RESULT_UNKNOWN);
489         }
490
491         private void finishWithResult(int result) {
492             if (mFinished) return;
493             boolean wasRegistered = mRegistered;
494             if (mRegistered) {
495                 mHandler.removeCallbacks(this);
496                 mSensorManager.unregisterListener(this);
497                 mDozeSensors.setDisableSensorsInterferingWithProximity(false);
498                 mRegistered = false;
499             }
500             onProximityResult(result);
501             if (wasRegistered) {
502                 mWakeLock.release(TAG);
503             }
504             mFinished = true;
505         }
506
507         @Override
508         public void onAccuracyChanged(Sensor sensor, int accuracy) {
509             // noop
510         }
511     }
512
513     private class TriggerReceiver extends BroadcastReceiver {
514         private boolean mRegistered;
515
516         @Override
517         public void onReceive(Context context, Intent intent) {
518             if (PULSE_ACTION.equals(intent.getAction())) {
519                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
520                 requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
521                         null /* onPulseSupressedListener */);
522             }
523             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
524                 mMachine.requestState(DozeMachine.State.FINISH);
525             }
526             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
527                 mDozeSensors.onUserSwitched();
528             }
529         }
530
531         public void register(Context context) {
532             if (mRegistered) {
533                 return;
534             }
535             IntentFilter filter = new IntentFilter(PULSE_ACTION);
536             filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
537             filter.addAction(Intent.ACTION_USER_SWITCHED);
538             context.registerReceiver(this, filter);
539             mRegistered = true;
540         }
541
542         public void unregister(Context context) {
543             if (!mRegistered) {
544                 return;
545             }
546             context.unregisterReceiver(this);
547             mRegistered = false;
548         }
549     }
550
551     private class DockEventListener implements DockManager.DockEventListener {
552         @Override
553         public void onEvent(int event) {
554             if (DEBUG) Log.d(TAG, "dock event = " + event);
555             switch (event) {
556                 case DockManager.STATE_DOCKED:
557                 case DockManager.STATE_DOCKED_HIDE:
558                     mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(true);
559                     break;
560                 case DockManager.STATE_NONE:
561                     mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(false);
562                     break;
563                 default:
564                     // no-op
565             }
566         }
567     }
568
569     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
570         @Override
571         public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
572             onNotification(onPulseSuppressedListener);
573         }
574
575         @Override
576         public void onPowerSaveChanged(boolean active) {
577             if (active) {
578                 mMachine.requestState(DozeMachine.State.FINISH);
579             }
580         }
581     };
582 }