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
This commit is contained in:
Dave Mankoff
2019-09-16 14:38:06 -04:00
parent a6efc88add
commit 63a1282b03
28 changed files with 976 additions and 490 deletions

View File

@@ -22,7 +22,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
* 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 {

View File

@@ -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.UserSwitcherController;
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 @@ public class Dependency {
@Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<FalsingManager> mFalsingManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
@Inject Lazy<AlarmManager> mAlarmManager;
@Inject
public Dependency() {
@@ -508,6 +510,7 @@ public class Dependency {
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

View File

@@ -23,6 +23,7 @@ import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
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 @@ public class DependencyProvider {
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
return new DeviceProvisionedControllerImpl(context, mainHandler);
}
/** */
@Singleton
@Provides
public AlarmManager provideAlarmManager(Context context) {
return context.getSystemService(AlarmManager.class);
}
}

View File

@@ -24,7 +24,7 @@ import com.android.systemui.assist.AssistModule;
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;

View File

@@ -43,7 +43,7 @@ import com.android.systemui.plugins.FalsingManager;
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;

View File

@@ -20,6 +20,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
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.FalsingPlugin;
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 @@ public class FalsingManagerProxy implements FalsingManager {
DeviceConfigProxy deviceConfig) {
mProximitySensor = proximitySensor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());

View File

@@ -30,7 +30,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
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;

View File

@@ -19,7 +19,7 @@ package com.android.systemui.classifier.brightline;
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;

View File

@@ -23,7 +23,7 @@ import android.provider.DeviceConfig;
import android.view.MotionEvent;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.ProximitySensor;
import com.android.systemui.util.sensors.ProximitySensor;
/**

View File

@@ -33,7 +33,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
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 @@ public class DozeFactory {
/** 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 @@ public class DozeFactory {
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) {

View File

@@ -26,7 +26,6 @@ import android.content.ContentResolver;
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 androidx.annotation.VisibleForTesting;
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 @@ public class DozeSensors {
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 @@ public class DozeSensors {
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 @@ public class DozeSensors {
mWakeLock = wakeLock;
mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
mCallback = callback;
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
@@ -146,8 +148,13 @@ public class DozeSensors {
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 class DozeSensors {
}
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 @@ public class DozeSensors {
/** 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

View File

@@ -24,10 +24,6 @@ import android.content.Context;
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 android.util.Log;
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 @@ public class DozeTriggers implements DozeMachine.Part {
*/
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 @@ public class DozeTriggers implements DozeMachine.Part {
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 class DozeTriggers implements DozeMachine.Part {
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 @@ public class DozeTriggers implements DozeMachine.Part {
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 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
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 @@ public class DozeTriggers implements DozeMachine.Part {
}
} else {
proximityCheckThenCall((result) -> {
if (result == ProximityCheck.RESULT_NEAR) {
if (result) {
// In pocket, drop event.
return;
}
@@ -267,7 +271,7 @@ public class DozeTriggers implements DozeMachine.Part {
if (wake) {
proximityCheckThenCall((result) -> {
if (result == ProximityCheck.RESULT_NEAR) {
if (result) {
// In pocket, drop event.
return;
}
@@ -376,7 +380,7 @@ public class DozeTriggers implements DozeMachine.Part {
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 @@ public class DozeTriggers implements DozeMachine.Part {
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;

View File

@@ -24,7 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
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,

View File

@@ -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);
}
}
}

View File

@@ -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 @@ public class AsyncSensorManager extends SensorManager
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

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -34,7 +34,7 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
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;

View File

@@ -30,7 +30,7 @@ import android.view.MotionEvent;
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;

View File

@@ -24,7 +24,7 @@ import static org.mockito.Mockito.when;
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;

View File

@@ -40,7 +40,7 @@ import androidx.test.filters.SmallTest;
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;

View File

@@ -44,7 +44,7 @@ import com.android.systemui.SysuiTestCase;
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;

View File

@@ -32,6 +32,7 @@ import android.hardware.display.AmbientDisplayConfiguration;
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 androidx.test.filters.SmallTest;
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 @@ public class DozeTriggersTest extends SysuiTestCase {
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 @@ public class DozeTriggersTest extends SysuiTestCase {
@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 @@ public class DozeTriggersTest extends SysuiTestCase {
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 @@ public class DozeTriggersTest extends SysuiTestCase {
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 @@ public class DozeTriggersTest extends SysuiTestCase {
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 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
verify(mDockManagerFake).removeListener(any());
}
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
}
}

View File

@@ -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.verify;
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 @@ import org.junit.runner.RunWith;
@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 class AsyncSensorManagerTest extends SysuiTestCase {
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 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener);
mAsyncSensorManager.waitUntilRequestsCompleted();
waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -85,7 +85,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
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 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
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();
}
}
}

View File

@@ -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 class FakeSensorManager extends SensorManager {
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 @@ public class FakeSensorManager extends SensorManager {
}
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 @@ public class FakeSensorManager extends SensorManager {
setter.invoke(sensor, type);
}
public class MockProximitySensor extends FakeGenericSensor {
public class FakeProximitySensor extends FakeGenericSensor {
private MockProximitySensor(Sensor sensor) {
private FakeProximitySensor(Sensor sensor) {
super(sensor);
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}