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