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