Make DozeSensors, DozeTriggers use ProximitySensor
This CL takes the features used in DozeSensors and applies
them to the publicly available util.sensors.ProximitySensor class.
It introduces a RateLimiter class for temprorarily suspending the
ProximitySensor as requested.
It introduces a ProximityCheck class for a quick, one time test
of the proximity sensor.
There is some reshuffling of Sensor related code to its own package
as well.
Bug: 141122220
Test: atest SystemUITests
Change-Id: If126be9a56c6966c1678ee70fa6f4623e51361a7
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index 60435d0..d62c1d4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -22,7 +22,7 @@
/**
* Allows for additional sensors to be retrieved from
- * {@link com.android.systemui.util.AsyncSensorManager}.
+ * {@link com.android.systemui.util.sensors.AsyncSensorManager}.
*/
@ProvidesInterface(action = SensorManagerPlugin.ACTION, version = SensorManagerPlugin.VERSION)
public interface SensorManagerPlugin extends Plugin {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bd5b9c7..ca6803d 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,6 +15,7 @@
package com.android.systemui;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
@@ -110,10 +111,10 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -314,6 +315,7 @@
@Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<FalsingManager> mFalsingManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
+ @Inject Lazy<AlarmManager> mAlarmManager;
@Inject
public Dependency() {
@@ -508,6 +510,7 @@
mProviders.put(INotificationManager.class, mINotificationManager::get);
mProviders.put(FalsingManager.class, mFalsingManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
+ mProviders.put(AlarmManager.class, mAlarmManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index d46a86c..239cbfe 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -23,6 +23,7 @@
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
@@ -230,4 +231,11 @@
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
return new DeviceProvisionedControllerImpl(context, mainHandler);
}
+
+ /** */
+ @Singleton
+ @Provides
+ public AlarmManager provideAlarmManager(Context context) {
+ return context.getSystemService(AlarmManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
index ff4eb83..38f2f5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
@@ -24,7 +24,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index c2b0fe4..20742d6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -43,7 +43,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 85bc22b..914258f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -20,6 +20,7 @@
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.content.Context;
+import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Handler;
import android.provider.DeviceConfig;
@@ -35,7 +36,7 @@
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
@@ -66,6 +67,7 @@
DeviceConfigProxy deviceConfig) {
mProximitySensor = proximitySensor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
+ mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 3f5cae6..0aa66af 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -30,7 +30,7 @@
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index bf39751..53ca783 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -19,7 +19,7 @@
import android.view.MotionEvent;
import com.android.systemui.classifier.Classifier;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index eeca409..f0feb4e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -23,7 +23,7 @@
import android.view.MotionEvent;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index c367286..bb8c7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,7 +33,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -45,7 +45,7 @@
/** Creates a DozeMachine with its parts for {@code dozeService}. */
public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) {
Context context = dozeService;
- SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
+ AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
DockManager dockManager = Dependency.get(DockManager.class);
WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
@@ -91,7 +91,7 @@
params.getPolicy());
}
- private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
+ private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
DockManager dockManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 026a625..7c1c040 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -43,11 +42,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.R;
+import com.android.systemui.Dependency;
import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AlarmTimeout;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.SensorRateLimiter;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -62,7 +63,7 @@
private final Context mContext;
private final AlarmManager mAlarmManager;
- private final SensorManager mSensorManager;
+ private final AsyncSensorManager mSensorManager;
private final ContentResolver mResolver;
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
@@ -74,13 +75,13 @@
protected TriggerSensor[] mSensors;
private final Handler mHandler = new Handler();
- private final ProxSensor mProxSensor;
+ private final ProximitySensor mProximitySensor;
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
private boolean mPaused;
- public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
+ public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
@@ -91,6 +92,7 @@
mWakeLock = wakeLock;
mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
+ mCallback = callback;
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
@@ -146,8 +148,13 @@
false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
};
- mProxSensor = new ProxSensor(policy);
- mCallback = callback;
+ mProximitySensor = new ProximitySensor(
+ context, sensorManager, Dependency.get(PluginManager.class));
+ new SensorRateLimiter(mProximitySensor, mAlarmManager, policy.proxCooldownTriggerMs,
+ policy.proxCooldownPeriodMs, "prox_sensor");
+
+ mProximitySensor.register(
+ proximityEvent -> mProxCallback.accept(!proximityEvent.getNear()));
}
/**
@@ -236,7 +243,15 @@
}
public void setProxListening(boolean listen) {
- mProxSensor.setRequested(listen);
+ if (mProximitySensor.isRegistered() && listen) {
+ mProximitySensor.alertListeners();
+ } else {
+ if (listen) {
+ mProximitySensor.resume();
+ } else {
+ mProximitySensor.pause();
+ }
+ }
}
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@@ -267,115 +282,16 @@
/** Dump current state */
public void dump(PrintWriter pw) {
for (TriggerSensor s : mSensors) {
- pw.print(" Sensor: "); pw.println(s.toString());
+ pw.println(" Sensor: " + s.toString());
}
- pw.print(" ProxSensor: "); pw.println(mProxSensor.toString());
+ pw.println(" ProxSensor: " + mProximitySensor.toString());
}
/**
- * @return true if prox is currently far, false if near or null if unknown.
+ * @return true if prox is currently near, false if far or null if unknown.
*/
- public Boolean isProximityCurrentlyFar() {
- return mProxSensor.mCurrentlyFar;
- }
-
- private class ProxSensor implements SensorEventListener {
-
- boolean mRequested;
- boolean mRegistered;
- Boolean mCurrentlyFar;
- long mLastNear;
- final AlarmTimeout mCooldownTimer;
- final AlwaysOnDisplayPolicy mPolicy;
- final Sensor mSensor;
- final boolean mUsingBrightnessSensor;
-
- public ProxSensor(AlwaysOnDisplayPolicy policy) {
- mPolicy = policy;
- mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
- "prox_cooldown", mHandler);
-
- // The default prox sensor can be noisy, so let's use a prox gated brightness sensor
- // if available.
- Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
- mContext.getString(R.string.doze_brightness_sensor_type));
- mUsingBrightnessSensor = sensor != null;
- if (sensor == null) {
- sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- }
- mSensor = sensor;
- }
-
- void setRequested(boolean requested) {
- if (mRequested == requested) {
- // Send an update even if we don't re-register.
- mHandler.post(() -> {
- if (mCurrentlyFar != null) {
- mProxCallback.accept(mCurrentlyFar);
- }
- });
- return;
- }
- mRequested = requested;
- updateRegistered();
- }
-
- private void updateRegistered() {
- setRegistered(mRequested && !mCooldownTimer.isScheduled());
- }
-
- private void setRegistered(boolean register) {
- if (mRegistered == register) {
- return;
- }
- if (register) {
- mRegistered = mSensorManager.registerListener(this, mSensor,
- SensorManager.SENSOR_DELAY_NORMAL, mHandler);
- } else {
- mSensorManager.unregisterListener(this);
- mRegistered = false;
- mCurrentlyFar = null;
- }
- }
-
- @Override
- public void onSensorChanged(android.hardware.SensorEvent event) {
- if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
-
- if (mUsingBrightnessSensor) {
- // The custom brightness sensor is gated by the proximity sensor and will return 0
- // whenever prox is covered.
- mCurrentlyFar = event.values[0] > 0;
- } else {
- mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
- }
- mProxCallback.accept(mCurrentlyFar);
-
- long now = SystemClock.elapsedRealtime();
- if (mCurrentlyFar == null) {
- // Sensor has been unregistered by the proxCallback. Do nothing.
- } else if (!mCurrentlyFar) {
- mLastNear = now;
- } else if (mCurrentlyFar && now - mLastNear < mPolicy.proxCooldownTriggerMs) {
- // If the last near was very recent, we might be using more power for prox
- // wakeups than we're saving from turning of the screen. Instead, turn it off
- // for a while.
- mCooldownTimer.schedule(mPolicy.proxCooldownPeriodMs,
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
- updateRegistered();
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public String toString() {
- return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s,"
- + " sensor=%s}", mRegistered, mRequested, mCooldownTimer.isScheduled(),
- mCurrentlyFar, mSensor);
- }
+ public Boolean isProximityCurrentlyNear() {
+ return mProximitySensor.isNear();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bab64db..80d4b63 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -24,10 +24,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -39,16 +35,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
-import java.util.function.IntConsumer;
+import java.util.function.Consumer;
/**
* Handles triggers for ambient state changes.
@@ -67,13 +63,15 @@
*/
private static boolean sWakeDisplaySensorState = true;
+ private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
+
private final Context mContext;
private final DozeMachine mMachine;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
private final AmbientDisplayConfiguration mConfig;
private final DozeParameters mDozeParameters;
- private final SensorManager mSensorManager;
+ private final AsyncSensorManager mSensorManager;
private final Handler mHandler;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
@@ -81,6 +79,7 @@
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
+ private final ProximitySensor.ProximityCheck mProxCheck;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -89,7 +88,7 @@
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
+ DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
mContext = context;
mMachine = machine;
@@ -105,6 +104,9 @@
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
+ mProxCheck = new ProximitySensor.ProximityCheck(
+ new ProximitySensor(mContext, mSensorManager, null),
+ mHandler);
}
private void onNotification(Runnable onPulseSuppressedListener) {
@@ -134,25 +136,27 @@
}
}
- private void proximityCheckThenCall(IntConsumer callback,
+ private void proximityCheckThenCall(Consumer<Boolean> callback,
boolean alreadyPerformedProxCheck,
int reason) {
- Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
+ Boolean cachedProxNear = mDozeSensors.isProximityCurrentlyNear();
if (alreadyPerformedProxCheck) {
- callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
- } else if (cachedProxFar != null) {
- callback.accept(cachedProxFar ? ProximityCheck.RESULT_FAR : ProximityCheck.RESULT_NEAR);
+ callback.accept(null);
+ } else if (cachedProxNear != null) {
+ callback.accept(cachedProxNear);
} else {
final long start = SystemClock.uptimeMillis();
- new ProximityCheck() {
- @Override
- public void onProximityResult(int result) {
- final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, reason);
- callback.accept(result);
- }
- }.check();
+ mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
+ final long end = SystemClock.uptimeMillis();
+ DozeLog.traceProximityResult(
+ mContext,
+ near == null ? false : near,
+ end - start,
+ reason);
+ callback.accept(near);
+ mWakeLock.release(TAG);
+ });
+ mWakeLock.acquire(TAG);
}
}
@@ -178,7 +182,7 @@
}
} else {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// In pocket, drop event.
return;
}
@@ -267,7 +271,7 @@
if (wake) {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// In pocket, drop event.
return;
}
@@ -376,7 +380,7 @@
mPulsePending = true;
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// in pocket, abort pulse
DozeLog.tracePulseDropped(mContext, "inPocket");
mPulsePending = false;
@@ -412,104 +416,11 @@
pw.print(" notificationPulseTime=");
pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
- pw.print(" pulsePending="); pw.println(mPulsePending);
+ pw.println(" pulsePending=" + mPulsePending);
pw.println("DozeSensors:");
mDozeSensors.dump(pw);
}
- /**
- * @see DozeSensors.ProxSensor
- */
- private abstract class ProximityCheck implements SensorEventListener, Runnable {
- private static final int TIMEOUT_DELAY_MS = 500;
-
- protected static final int RESULT_UNKNOWN = 0;
- protected static final int RESULT_NEAR = 1;
- protected static final int RESULT_FAR = 2;
- protected static final int RESULT_NOT_CHECKED = 3;
-
- private boolean mRegistered;
- private boolean mFinished;
- private float mMaxRange;
- private boolean mUsingBrightnessSensor;
-
- protected abstract void onProximityResult(int result);
-
- public void check() {
- Preconditions.checkState(!mFinished && !mRegistered);
- Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
- mContext.getString(R.string.doze_brightness_sensor_type));
- mUsingBrightnessSensor = sensor != null;
- if (sensor == null) {
- sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- }
- if (sensor == null) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
- finishWithResult(RESULT_UNKNOWN);
- return;
- }
- mDozeSensors.setDisableSensorsInterferingWithProximity(true);
-
- mMaxRange = sensor.getMaximumRange();
- mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
- mHandler);
- mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
- mWakeLock.acquire(TAG);
- mRegistered = true;
- }
-
- /**
- * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.values.length == 0) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
- finishWithResult(RESULT_UNKNOWN);
- } else {
- if (DozeMachine.DEBUG) {
- Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
- }
- final boolean isNear;
- if (mUsingBrightnessSensor) {
- // The custom brightness sensor is gated by the proximity sensor and will
- // return 0 whenever prox is covered.
- isNear = event.values[0] == 0;
- } else {
- isNear = event.values[0] < mMaxRange;
- }
- finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
- }
- }
-
- @Override
- public void run() {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
- finishWithResult(RESULT_UNKNOWN);
- }
-
- private void finishWithResult(int result) {
- if (mFinished) return;
- boolean wasRegistered = mRegistered;
- if (mRegistered) {
- mHandler.removeCallbacks(this);
- mSensorManager.unregisterListener(this);
- mDozeSensors.setDisableSensorsInterferingWithProximity(false);
- mRegistered = false;
- }
- onProximityResult(result);
- if (wasRegistered) {
- mWakeLock.release(TAG);
- }
- mFinished = true;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // noop
- }
- }
-
private class TriggerReceiver extends BroadcastReceiver {
private boolean mRegistered;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index f7bb97b..00b764b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -24,7 +24,7 @@
import com.android.systemui.Dependency
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
-import com.android.systemui.util.AsyncSensorManager
+import com.android.systemui.util.sensors.AsyncSensorManager
class KeyguardLiftController constructor(
private val statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
deleted file mode 100644
index a905eba..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.util.Log;
-
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Simple wrapper around SensorManager customized for the Proximity sensor.
- */
-public class ProximitySensor {
- private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = false;
-
- private final Sensor mSensor;
- private final AsyncSensorManager mSensorManager;
- private final boolean mUsingBrightnessSensor;
- private final float mMaxRange;
-
- private SensorEventListener mSensorEventListener = new SensorEventListener() {
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- onSensorEvent(event);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- };
- private boolean mNear;
- private List<ProximitySensorListener> mListeners = new ArrayList<>();
- private String mTag = null;
-
- @Inject
- public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
- mSensorManager = sensorManager;
- Sensor sensor = findBrightnessSensor(context, sensorManager);
-
- if (sensor == null) {
- mUsingBrightnessSensor = false;
- sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- } else {
- mUsingBrightnessSensor = true;
- }
- mSensor = sensor;
- if (mSensor != null) {
- mMaxRange = mSensor.getMaximumRange();
- } else {
- mMaxRange = 0;
- }
- }
-
- public void setTag(String tag) {
- mTag = tag;
- }
-
- private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) {
- String sensorType = context.getString(R.string.doze_brightness_sensor_type);
- List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
- Sensor sensor = null;
- for (Sensor s : sensorList) {
- if (sensorType.equals(s.getStringType())) {
- sensor = s;
- break;
- }
- }
-
- return sensor;
- }
-
- /**
- * Returns {@code false} if a Proximity sensor is not available.
- */
- public boolean getSensorAvailable() {
- return mSensor != null;
- }
-
- /**
- * Add a listener.
- *
- * Registers itself with the {@link SensorManager} if this is the first listener
- * added.
- */
- public boolean register(ProximitySensorListener listener) {
- if (!getSensorAvailable()) {
- return false;
- }
-
- logDebug("using brightness sensor? " + mUsingBrightnessSensor);
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- logDebug("registering sensor listener");
- mSensorManager.registerListener(
- mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
- }
-
- return true;
- }
-
- /**
- * Remove a listener.
- *
- * If all listeners are removed from an instance of this class,
- * it will unregister itself with the SensorManager.
- */
- public void unregister(ProximitySensorListener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- logDebug("unregistering sensor listener");
- mSensorManager.unregisterListener(mSensorEventListener);
- }
- }
-
- public boolean isNear() {
- return getSensorAvailable() && mNear;
- }
-
- private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mMaxRange;
- if (mUsingBrightnessSensor) {
- near = event.values[0] == 0;
- }
- mNear = near;
- mListeners.forEach(proximitySensorListener ->
- proximitySensorListener.onProximitySensorEvent(
- new ProximityEvent(mNear, event.timestamp)));
- }
-
- /** Implement to be notified of ProximityEvents. */
- public interface ProximitySensorListener {
- /** Called when the ProximitySensor changes. */
- void onProximitySensorEvent(ProximityEvent proximityEvent);
- }
-
- /**
- * Returned when the near/far state of a {@link ProximitySensor} changes.
- */
- public static class ProximityEvent {
- private final boolean mNear;
- private final long mTimestampNs;
-
- public ProximityEvent(boolean near, long timestampNs) {
- mNear = near;
- mTimestampNs = timestampNs;
- }
-
- public boolean getNear() {
- return mNear;
- }
-
- public long getTimestampNs() {
- return mTimestampNs;
- }
- }
-
- private void logDebug(String msg) {
- if (DEBUG) {
- Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
rename to packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index b9c5ee5..dcd0c58 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -56,23 +56,31 @@
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
- private final HandlerThread mHandlerThread = new HandlerThread("async_sensor");
- @VisibleForTesting final Handler mHandler;
+ private final Handler mHandler;
private final List<SensorManagerPlugin> mPlugins;
@Inject
public AsyncSensorManager(Context context, PluginManager pluginManager) {
- this(context.getSystemService(SensorManager.class), pluginManager);
+ this(context.getSystemService(SensorManager.class), pluginManager, null);
}
@VisibleForTesting
- AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+ public AsyncSensorManager(
+ SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
mInner = sensorManager;
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ if (handler == null) {
+ HandlerThread handlerThread = new HandlerThread("async_sensor");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ } else {
+ mHandler = handler;
+ }
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
- pluginManager.addPluginListener(this, SensorManagerPlugin.class, true /* allowMultiple */);
+ if (pluginManager != null) {
+ pluginManager.addPluginListener(this, SensorManagerPlugin.class,
+ true /* allowMultiple */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/LimitableSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/LimitableSensor.java
new file mode 100644
index 0000000..2dfd187
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/LimitableSensor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+/**
+ * Represents a sensor that can be throttled by a {@link SensorRateLimiter}.
+ */
+public interface LimitableSensor {
+ /**
+ * Allows a sensor to be temporarily throttled or disabled.
+ */
+ void setRateLimited(boolean limited);
+
+ /**
+ * Registers a {@link SensorRateLimiter} with the sensor.
+ */
+ void setRateLimiter(SensorRateLimiter rateLimiter);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
new file mode 100644
index 0000000..1eed566
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.plugins.PluginManager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Simple wrapper around SensorManager customized for the Proximity sensor.
+ */
+public class ProximitySensor implements LimitableSensor {
+ private static final String TAG = "ProxSensor";
+ private static final boolean DEBUG = false;
+
+ private final Sensor mSensor;
+ private final AsyncSensorManager mSensorManager;
+ private final boolean mUsingBrightnessSensor;
+ private final float mMaxRange;
+ private List<ProximitySensorListener> mListeners = new ArrayList<>();
+ private String mTag = null;
+ private ProximityEvent mLastEvent;
+ private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
+ private boolean mPaused;
+ private boolean mRegistered;
+ private SensorRateLimiter mRateLimiter;
+ private boolean mRateLimited = false;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ @Inject
+ public ProximitySensor(
+ Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) {
+ mSensorManager = sensorManager;
+ Sensor sensor = findBrightnessSensor(context);
+
+ if (sensor == null) {
+ mUsingBrightnessSensor = false;
+ sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ } else {
+ mUsingBrightnessSensor = true;
+ }
+ mSensor = sensor;
+ if (mSensor != null) {
+ mMaxRange = mSensor.getMaximumRange();
+ } else {
+ mMaxRange = 0;
+ }
+ }
+
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ public void setSensorDelay(int sensorDelay) {
+ mSensorDelay = sensorDelay;
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ public void pause() {
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ public void resume() {
+ mPaused = false;
+ registerInternal();
+ }
+
+ /**
+ * Supply a {@link SensorRateLimiter} to help limit power usage.
+ */
+ @Override
+ public void setRateLimiter(SensorRateLimiter rateLimiter) {
+ mRateLimiter = rateLimiter;
+ }
+
+ private Sensor findBrightnessSensor(Context context) {
+ String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+
+ /**
+ * Registers/unregisters the proximity sensor with the SensorManager.
+ */
+ @Override
+ public void setRateLimited(boolean limited) {
+ mRateLimited = limited;
+ if (mRateLimited) {
+ unregisterInternal();
+ } else {
+ registerInternal();
+ }
+ }
+
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Returns {@code false} if a Proximity sensor is not available.
+ */
+ public boolean getSensorAvailable() {
+ return mSensor != null;
+ }
+
+ /**
+ * Add a listener.
+ *
+ * Registers itself with the {@link SensorManager} if this is the first listener
+ * added. If a cool down is currently running, the sensor will be registered when it is over.
+ */
+ public boolean register(ProximitySensorListener listener) {
+ if (!getSensorAvailable()) {
+ return false;
+ }
+
+ logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
+ mListeners.add(listener);
+ registerInternal();
+
+ return true;
+ }
+
+ private void registerInternal() {
+ if (mRegistered || mRateLimited || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ logDebug("Registering sensor listener");
+ mRegistered = true;
+ mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * If all listeners are removed from an instance of this class,
+ * it will unregister itself with the SensorManager.
+ */
+ public void unregister(ProximitySensorListener listener) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ unregisterInternal();
+ }
+ }
+
+ private void unregisterInternal() {
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("unregistering sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mRegistered = false;
+ }
+
+ public Boolean isNear() {
+ return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
+ }
+
+ /** Update all listeners with the last value this class received from the sensor. */
+ public void alertListeners() {
+ mListeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onSensorEvent(mLastEvent));
+ }
+
+ private void onSensorEvent(SensorEvent event) {
+ boolean near = event.values[0] < mMaxRange;
+ if (mUsingBrightnessSensor) {
+ near = event.values[0] == 0;
+ }
+ mLastEvent = new ProximityEvent(near, event.timestamp);
+ alertListeners();
+ if (mRateLimiter != null) {
+ mRateLimiter.onSensorEvent(mLastEvent.getTimestampMs());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, near=%s, rateLimiter=%s, sensor=%s}",
+ isRegistered(), mPaused, isNear(), mRateLimiter, mSensor);
+ }
+
+ /**
+ * Convenience class allowing for briefly checking the proximity sensor.
+ */
+ public static class ProximityCheck implements Runnable {
+
+ private final ProximitySensor mSensor;
+ private final Handler mHandler;
+ private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
+
+ @Inject
+ public ProximityCheck(ProximitySensor sensor, Handler handler) {
+ mSensor = sensor;
+ mSensor.setTag("prox_check");
+ mHandler = handler;
+ mSensor.pause();
+ ProximitySensorListener listener = proximityEvent -> {
+ mCallbacks.forEach(
+ booleanConsumer ->
+ booleanConsumer.accept(
+ proximityEvent == null ? null : proximityEvent.getNear()));
+ mCallbacks.clear();
+ mSensor.pause();
+ };
+ mSensor.register(listener);
+ }
+
+ /** Set a descriptive tag for the sensors registration. */
+ public void setTag(String tag) {
+ mSensor.setTag(tag);
+ }
+
+ @Override
+ public void run() {
+ mSensor.pause();
+ mSensor.alertListeners();
+ }
+
+ /**
+ * Query the proximity sensor, timing out if no result.
+ */
+ public void check(long timeoutMs, Consumer<Boolean> callback) {
+ if (!mSensor.getSensorAvailable()) {
+ callback.accept(null);
+ }
+ mCallbacks.add(callback);
+ if (!mSensor.isRegistered()) {
+ mSensor.resume();
+ mHandler.postDelayed(this, timeoutMs);
+ }
+ }
+ }
+
+ /** Implement to be notified of ProximityEvents. */
+ public interface ProximitySensorListener {
+ /** Called when the ProximitySensor changes. */
+ void onSensorEvent(ProximityEvent proximityEvent);
+ }
+
+ /**
+ * Returned when the near/far state of a {@link ProximitySensor} changes.
+ */
+ public static class ProximityEvent {
+ private final boolean mNear;
+ private final long mTimestampNs;
+
+ public ProximityEvent(boolean near, long timestampNs) {
+ mNear = near;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getNear() {
+ return mNear;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
+ }
+
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorRateLimiter.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorRateLimiter.java
new file mode 100644
index 0000000..00f5bb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorRateLimiter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.app.AlarmManager;
+import android.os.Handler;
+
+import com.android.systemui.util.AlarmTimeout;
+
+/**
+ * Monitors a sensor for over-activity and can throttle it down as needed.
+ *
+ * Currently, this class is designed with the {@link ProximitySensor} in mind.
+ */
+public class SensorRateLimiter {
+ private final LimitableSensor mSensor;
+ private final AlarmTimeout mCooldownTimer;
+ private final long mCoolDownTriggerMs;
+ private final long mCoolDownPeriodMs;
+ private long mLastTimestampMs;
+
+ public SensorRateLimiter(
+ LimitableSensor sensor,
+ AlarmManager alarmManager,
+ long coolDownTriggerMs,
+ long coolDownPeriodMs,
+ String alarmTag) {
+
+ mSensor = sensor;
+ mCoolDownTriggerMs = coolDownTriggerMs;
+ mCoolDownPeriodMs = coolDownPeriodMs;
+ mLastTimestampMs = -1;
+
+ Handler handler = new Handler();
+ mCooldownTimer = new AlarmTimeout(alarmManager, this::coolDownComplete, alarmTag, handler);
+
+ mSensor.setRateLimiter(this);
+ }
+
+
+ void onSensorEvent(long timestampMs) {
+ if (mLastTimestampMs >= 0 && mCoolDownTriggerMs > 0 && mCoolDownPeriodMs > 0
+ && timestampMs - mLastTimestampMs < mCoolDownTriggerMs) {
+ scheduleCoolDown();
+ }
+
+ mLastTimestampMs = timestampMs;
+ }
+
+ private void scheduleCoolDown() {
+ mSensor.setRateLimited(true);
+ mCooldownTimer.schedule(mCoolDownPeriodMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ }
+
+ private void coolDownComplete() {
+ mSensor.setRateLimited(false);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{coolingDown=%s}", mCooldownTimer.isScheduled());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index b979356..3561e34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -34,7 +34,7 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 3fc5d72..35d59c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -30,7 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 0c124ff..752e145 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -24,7 +24,7 @@
import static org.mockito.Mockito.withSettings;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.mockito.Answers;
import org.mockito.MockSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 392c677..aa62e9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -40,7 +40,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index cd6d1e0..ddd1685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -44,7 +44,7 @@
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e190f99..f7cd696 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -32,6 +32,7 @@
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
@@ -39,9 +40,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -55,12 +57,8 @@
private DozeTriggers mTriggers;
private DozeMachine mMachine;
private DozeHostFake mHost;
- private AmbientDisplayConfiguration mConfig;
- private DozeParameters mParameters;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
- private WakeLock mWakeLock;
- private AlarmManager mAlarmManager;
private DockManager mDockManagerFake;
@BeforeClass
@@ -72,18 +70,21 @@
@Before
public void setUp() throws Exception {
mMachine = mock(DozeMachine.class);
- mAlarmManager = mock(AlarmManager.class);
+ AlarmManager alarmManager = mock(AlarmManager.class);
mHost = spy(new DozeHostFake());
- mConfig = DozeConfigurationUtil.createMockConfig();
- mParameters = DozeConfigurationUtil.createMockParameters();
+ AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
+ DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
mSensors = spy(new FakeSensorManager(mContext));
mTapSensor = mSensors.getFakeTapSensor().getSensor();
- mWakeLock = new WakeLockFake();
+ WakeLock wakeLock = new WakeLockFake();
mDockManagerFake = mock(DockManager.class);
+ AsyncSensorManager asyncSensorManager =
+ new AsyncSensorManager(mSensors, null, new Handler());
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
- mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
+ mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+ asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
mDockManagerFake);
+ waitForSensorManager();
}
@Test
@@ -95,13 +96,14 @@
clearInvocations(mMachine);
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
+ mSensors.getFakeProximitySensor().sendProximityResult(false); /* Near */
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
+ waitForSensorManager();
+ mSensors.getFakeProximitySensor().sendProximityResult(true); /* Far */
verify(mMachine).requestPulse(anyInt());
}
@@ -111,6 +113,7 @@
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
@@ -118,10 +121,12 @@
DozeMachine.State.DOZE_REQUEST_PULSE);
mTriggers.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
+ waitForSensorManager();
verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
@@ -133,4 +138,8 @@
mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
verify(mDockManagerFake).removeListener(any());
}
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 4a9b1b3..9149599 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -24,15 +23,15 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
@@ -40,20 +39,21 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
- private TestableAsyncSensorManager mAsyncSensorManager;
- private FakeSensorManager mFakeSensorManager;
+ private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
- private FakeSensorManager.MockProximitySensor mSensor;
+ private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
- mFakeSensorManager = new FakeSensorManager(mContext);
- mAsyncSensorManager = new TestableAsyncSensorManager(mFakeSensorManager);
- mSensor = mFakeSensorManager.getMockProximitySensor();
+ FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
+ mAsyncSensorManager = new AsyncSensorManager(
+ fakeSensorManager, mPluginManager, new Handler());
+ mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -61,7 +61,7 @@
public void registerListenerImpl() throws Exception {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was registered.
mSensor.sendProximityResult(true);
@@ -73,7 +73,7 @@
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -85,7 +85,7 @@
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener, mSensor.getSensor());
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -98,13 +98,7 @@
eq(SensorManagerPlugin.class), eq(true) /* allowMultiple */);
}
- private class TestableAsyncSensorManager extends AsyncSensorManager {
- public TestableAsyncSensorManager(SensorManager sensorManager) {
- super(sensorManager, mPluginManager);
- }
-
- public void waitUntilRequestsCompleted() {
- assertTrue(mHandler.runWithScissors(() -> {}, 0));
- }
+ public void waitUntilRequestsCompleted() {
+ TestableLooper.get(this).processAllMessages();
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 29b8ab60..1deb495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.utils.hardware;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -54,7 +54,7 @@
public static final String TAP_SENSOR_TYPE = "tapSensorType";
- private final MockProximitySensor mMockProximitySensor;
+ private final FakeProximitySensor mFakeProximitySensor;
private final FakeGenericSensor mFakeLightSensor;
private final FakeGenericSensor mFakeTapSensor;
private final FakeGenericSensor[] mSensors;
@@ -68,14 +68,14 @@
}
mSensors = new FakeGenericSensor[]{
- mMockProximitySensor = new MockProximitySensor(proxSensor),
+ mFakeProximitySensor = new FakeProximitySensor(proxSensor),
mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT, null)),
mFakeTapSensor = new FakeGenericSensor(createSensor(99, TAP_SENSOR_TYPE))
};
}
- public MockProximitySensor getMockProximitySensor() {
- return mMockProximitySensor;
+ public FakeProximitySensor getFakeProximitySensor() {
+ return mFakeProximitySensor;
}
public FakeGenericSensor getFakeLightSensor() {
@@ -231,9 +231,9 @@
setter.invoke(sensor, type);
}
- public class MockProximitySensor extends FakeGenericSensor {
+ public class FakeProximitySensor extends FakeGenericSensor {
- private MockProximitySensor(Sensor sensor) {
+ private FakeProximitySensor(Sensor sensor) {
super(sensor);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
new file mode 100644
index 0000000..9c316c5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximitySensorTest extends SysuiTestCase {
+
+ private ProximitySensor mProximitySensor;
+ private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+
+ @Before
+ public void setUp() throws Exception {
+ FakeSensorManager sensorManager = new FakeSensorManager(getContext());
+ AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
+ sensorManager, null, new Handler());
+ mFakeProximitySensor = sensorManager.getFakeProximitySensor();
+ mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager, null);
+ }
+
+ @Test
+ public void testSingleListener() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testMultiListener() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mLastEvent.getNear());
+ assertFalse(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 1);
+ assertEquals(listenerB.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testUnregister() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testPauseAndResume() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.pause();
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+
+ // More events do nothing when paused.
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.resume();
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ // Still matches our previous call
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+
+ @Test
+ public void testRateLimit() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.setRateLimited(true);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+
+ // More events do nothing when rate limited.
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.setRateLimited(false);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ // Still matches our previous call
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testAlertListeners() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mProximitySensor.alertListeners();
+ assertNull(listenerA.mLastEvent);
+ assertEquals(listenerA.mCallCount, 1);
+ assertNull(listenerB.mLastEvent);
+ assertEquals(listenerB.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ class TestableListener implements ProximitySensor.ProximitySensorListener {
+ ProximitySensor.ProximityEvent mLastEvent;
+ int mCallCount = 0;
+
+ @Override
+ public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ mLastEvent = proximityEvent;
+ mCallCount++;
+ }
+
+ void reset() {
+ mLastEvent = null;
+ mCallCount = 0;
+ }
+ };
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/RateLimiterTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/RateLimiterTest.java
new file mode 100644
index 0000000..cd43cf7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/RateLimiterTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.AlarmTimeout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class RateLimiterTest extends SysuiTestCase {
+
+ private static final long COOL_DOWN_TRIGGER_MS = 100;
+ private static final long COOL_DOWN_PERIOD_MS = 200;
+ private static final String ALARM_TAG = "rate_limiter_test";
+
+ @Mock
+ LimitableSensor mSensor;
+ @Mock
+ private AlarmManager mAlarmManager;
+
+ private SensorRateLimiter mSensorRateLimiter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSensorRateLimiter = new SensorRateLimiter(mSensor, mAlarmManager, COOL_DOWN_TRIGGER_MS,
+ COOL_DOWN_PERIOD_MS, ALARM_TAG);
+ }
+
+ @Test
+ public void testInfrequentEvents() {
+ mSensorRateLimiter.onSensorEvent(0);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 2);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 4);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 8);
+
+ verify(mSensor, never()).setRateLimited(true);
+ }
+
+ @Test
+ public void testRateLimit() {
+ mSensorRateLimiter.onSensorEvent(0);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS - 1);
+
+ verify(mSensor).setRateLimited(true);
+ verify(mAlarmManager).setExact(
+ anyInt(), anyLong(), eq(ALARM_TAG), any(AlarmTimeout.class), any(Handler.class));
+ }
+
+ @Test
+ public void testSlowToOverTrigger() {
+ mSensorRateLimiter.onSensorEvent(0);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 2);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 4);
+
+ verify(mSensor, never()).setRateLimited(true);
+
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS * 5 - 1);
+
+ verify(mSensor).setRateLimited(true);
+ verify(mAlarmManager).setExact(
+ anyInt(), anyLong(), eq(ALARM_TAG), any(AlarmTimeout.class), any(Handler.class));
+ }
+
+ @Test
+ public void testCoolDownComplete() {
+ mSensorRateLimiter.onSensorEvent(0);
+ mSensorRateLimiter.onSensorEvent(COOL_DOWN_TRIGGER_MS - 1);
+
+ ArgumentCaptor<AlarmManager.OnAlarmListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+
+ verify(mSensor).setRateLimited(true);
+ verify(mAlarmManager).setExact(
+ anyInt(), anyLong(), eq(ALARM_TAG), listenerArgumentCaptor.capture(),
+ any(Handler.class));
+
+ listenerArgumentCaptor.getValue().onAlarm();
+ verify(mSensor).setRateLimited(false);
+ }
+}