DozeSensors.java revision 79bacbbbdb4a2aa4cf6d143c2d2c30d7dd31ace5
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 com.android.internal.hardware.AmbientDisplayConfiguration;
20import com.android.internal.logging.MetricsLogger;
21import com.android.internal.logging.MetricsProto;
22import com.android.systemui.statusbar.phone.DozeParameters;
23
24import android.annotation.AnyThread;
25import android.app.ActivityManager;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.database.ContentObserver;
29import android.hardware.Sensor;
30import android.hardware.SensorManager;
31import android.hardware.TriggerEvent;
32import android.hardware.TriggerEventListener;
33import android.media.AudioAttributes;
34import android.net.Uri;
35import android.os.Handler;
36import android.os.PowerManager;
37import android.os.SystemClock;
38import android.os.UserHandle;
39import android.os.Vibrator;
40import android.provider.Settings;
41import android.text.TextUtils;
42import android.util.Log;
43
44import java.util.List;
45
46public class DozeSensors {
47
48    private static final boolean DEBUG = DozeService.DEBUG;
49
50    private static final String TAG = "DozeSensors";
51
52    private final Context mContext;
53    private final SensorManager mSensorManager;
54    private final TriggerSensor[] mSensors;
55    private final ContentResolver mResolver;
56    private final TriggerSensor mPickupSensor;
57    private final DozeParameters mDozeParameters;
58    private final AmbientDisplayConfiguration mConfig;
59    private final PowerManager.WakeLock mWakeLock;
60    private final Callback mCallback;
61
62    private final Handler mHandler = new Handler();
63
64
65    public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
66            AmbientDisplayConfiguration config, PowerManager.WakeLock wakeLock, Callback callback) {
67        mContext = context;
68        mSensorManager = sensorManager;
69        mDozeParameters = dozeParameters;
70        mConfig = config;
71        mWakeLock = wakeLock;
72        mResolver = mContext.getContentResolver();
73
74        mSensors = new TriggerSensor[] {
75                new TriggerSensor(
76                        mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
77                        null /* setting */,
78                        dozeParameters.getPulseOnSigMotion(),
79                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION),
80                mPickupSensor = new TriggerSensor(
81                        mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
82                        Settings.Secure.DOZE_PULSE_ON_PICK_UP,
83                        config.pulseOnPickupAvailable(),
84                        DozeLog.PULSE_REASON_SENSOR_PICKUP),
85                new TriggerSensor(
86                        findSensorWithType(config.doubleTapSensorType()),
87                        Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
88                        true /* configured */,
89                        DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
90        };
91        mCallback = callback;
92    }
93
94    private Sensor findSensorWithType(String type) {
95        if (TextUtils.isEmpty(type)) {
96            return null;
97        }
98        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
99        for (Sensor s : sensorList) {
100            if (type.equals(s.getStringType())) {
101                return s;
102            }
103        }
104        return null;
105    }
106
107    public void setListen(boolean listen) {
108        for (TriggerSensor s : mSensors) {
109            s.setListening(listen);
110            if (listen) {
111                s.registerSettingsObserver(mSettingsObserver);
112            }
113        }
114        if (!listen) {
115            mResolver.unregisterContentObserver(mSettingsObserver);
116        }
117    }
118
119    public void reregisterAllSensors() {
120        for (TriggerSensor s : mSensors) {
121            s.setListening(false);
122        }
123        for (TriggerSensor s : mSensors) {
124            s.setListening(true);
125        }
126    }
127
128    public void onUserSwitched() {
129        for (TriggerSensor s : mSensors) {
130            s.updateListener();
131        }
132    }
133
134    private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
135        @Override
136        public void onChange(boolean selfChange, Uri uri, int userId) {
137            if (userId != ActivityManager.getCurrentUser()) {
138                return;
139            }
140            for (TriggerSensor s : mSensors) {
141                s.updateListener();
142            }
143        }
144    };
145
146    public void setDisableSensorsInterferingWithProximity(boolean disable) {
147        mPickupSensor.setDisabled(disable);
148    }
149
150    private class TriggerSensor extends TriggerEventListener {
151        final Sensor mSensor;
152        final boolean mConfigured;
153        final int mPulseReason;
154        final String mSetting;
155
156        private boolean mRequested;
157        private boolean mRegistered;
158        private boolean mDisabled;
159
160        public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason) {
161            mSensor = sensor;
162            mSetting = setting;
163            mConfigured = configured;
164            mPulseReason = pulseReason;
165        }
166
167        public void setListening(boolean listen) {
168            if (mRequested == listen) return;
169            mRequested = listen;
170            updateListener();
171        }
172
173        public void setDisabled(boolean disabled) {
174            if (mDisabled == disabled) return;
175            mDisabled = disabled;
176            updateListener();
177        }
178
179        public void updateListener() {
180            if (!mConfigured || mSensor == null) return;
181            if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
182                mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
183                if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered);
184            } else if (mRegistered) {
185                final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor);
186                if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt);
187                mRegistered = false;
188            }
189        }
190
191        private boolean enabledBySetting() {
192            if (TextUtils.isEmpty(mSetting)) {
193                return true;
194            }
195            return Settings.Secure.getIntForUser(mResolver, mSetting, 1,
196                    UserHandle.USER_CURRENT) != 0;
197        }
198
199        @Override
200        public String toString() {
201            return new StringBuilder("{mRegistered=").append(mRegistered)
202                    .append(", mRequested=").append(mRequested)
203                    .append(", mDisabled=").append(mDisabled)
204                    .append(", mConfigured=").append(mConfigured)
205                    .append(", mSensor=").append(mSensor).append("}").toString();
206        }
207
208        @Override
209        @AnyThread
210        public void onTrigger(TriggerEvent event) {
211            mHandler.post(mWakeLock.wrap(() -> {
212                if (DEBUG)
213                    Log.d(TAG, "onTrigger: " + triggerEventToString(event));
214                boolean sensorPerformsProxCheck = false;
215                if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
216                    int subType = (int) event.values[0];
217                    MetricsLogger.action(
218                            mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE,
219                            subType);
220                    sensorPerformsProxCheck =
221                            mDozeParameters.getPickupSubtypePerformsProxCheck(subType);
222                }
223
224                mRegistered = false;
225                mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck);
226                updateListener();  // reregister, this sensor only fires once
227            }));
228        }
229
230        public void registerSettingsObserver(ContentObserver settingsObserver) {
231            if (mConfigured && !TextUtils.isEmpty(mSetting)) {
232                mResolver.registerContentObserver(
233                        Settings.Secure.getUriFor(mSetting), false /* descendants */,
234                        mSettingsObserver, UserHandle.USER_ALL);
235            }
236        }
237
238        private String triggerEventToString(TriggerEvent event) {
239            if (event == null) return null;
240            final StringBuilder sb = new StringBuilder("TriggerEvent[")
241                    .append(event.timestamp).append(',')
242                    .append(event.sensor.getName());
243            if (event.values != null) {
244                for (int i = 0; i < event.values.length; i++) {
245                    sb.append(',').append(event.values[i]);
246                }
247            }
248            return sb.append(']').toString();
249        }
250    }
251
252    public interface Callback {
253        void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck);
254    }
255}
256