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