Prevent recursive calls to proximity sensor.
This CL contains two main changes: 1) If a caller asks an instance of ProximitySensor to alert its listeners recursively, the call will be ignored. 2) The DozeSensors and DozeTriggers both use the same instance of a ProximitySensor so that they can share each other's prox calls. Also, a DelayableExecutor is now passed into ProximityCheck where once we used a Handler in order to make testing easier. Fixes: 152821688 Test: atest SystemUITests Change-Id: I8be62222c4e2b586218696b1d8b6ac158ebeffb5 Merged-In: I8be62222c4e2b586218696b1d8b6ac158ebeffb5
This commit is contained in:
@@ -34,6 +34,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.statusbar.policy.BatteryController;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
import com.android.systemui.util.sensors.ProximitySensor;
|
||||
import com.android.systemui.util.wakelock.DelayedWakeLock;
|
||||
@@ -56,6 +57,7 @@ public class DozeFactory {
|
||||
private final ProximitySensor mProximitySensor;
|
||||
private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
|
||||
private final Handler mHandler;
|
||||
private final DelayableExecutor mDelayableExecutor;
|
||||
private final BiometricUnlockController mBiometricUnlockController;
|
||||
private final BroadcastDispatcher mBroadcastDispatcher;
|
||||
private final DozeHost mDozeHost;
|
||||
@@ -68,6 +70,7 @@ public class DozeFactory {
|
||||
DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
|
||||
ProximitySensor proximitySensor,
|
||||
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
|
||||
DelayableExecutor delayableExecutor,
|
||||
BiometricUnlockController biometricUnlockController,
|
||||
BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
|
||||
mFalsingManager = falsingManager;
|
||||
@@ -83,6 +86,7 @@ public class DozeFactory {
|
||||
mProximitySensor = proximitySensor;
|
||||
mDelayedWakeLockBuilder = delayedWakeLockBuilder;
|
||||
mHandler = handler;
|
||||
mDelayableExecutor = delayableExecutor;
|
||||
mBiometricUnlockController = biometricUnlockController;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
mDozeHost = dozeHost;
|
||||
@@ -107,8 +111,8 @@ public class DozeFactory {
|
||||
new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
|
||||
new DozeFalsingManagerAdapter(mFalsingManager),
|
||||
createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost,
|
||||
mAlarmManager, config, mDozeParameters, mHandler, wakeLock, machine,
|
||||
mDockManager, mDozeLog),
|
||||
mAlarmManager, config, mDozeParameters, mDelayableExecutor, wakeLock,
|
||||
machine, mDockManager, mDozeLog),
|
||||
createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler,
|
||||
mAlarmManager, mDozeParameters, mDozeLog),
|
||||
new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters,
|
||||
@@ -135,11 +139,11 @@ public class DozeFactory {
|
||||
|
||||
private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
|
||||
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
|
||||
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
|
||||
DockManager dockManager, DozeLog dozeLog) {
|
||||
DozeParameters params, DelayableExecutor delayableExecutor, WakeLock wakeLock,
|
||||
DozeMachine machine, DockManager dockManager, DozeLog dozeLog) {
|
||||
boolean allowPulseTriggers = true;
|
||||
return new DozeTriggers(context, machine, host, alarmManager, config, params,
|
||||
sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
|
||||
sensorManager, delayableExecutor, wakeLock, allowPulseTriggers, dockManager,
|
||||
mProximitySensor, dozeLog, mBroadcastDispatcher);
|
||||
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ public class DozeSensors {
|
||||
mProxCallback = proxCallback;
|
||||
mResolver = mContext.getContentResolver();
|
||||
mCallback = callback;
|
||||
mProximitySensor = proximitySensor;
|
||||
|
||||
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
|
||||
mSensors = new TriggerSensor[] {
|
||||
@@ -172,7 +173,6 @@ public class DozeSensors {
|
||||
dozeLog),
|
||||
};
|
||||
|
||||
mProximitySensor = proximitySensor;
|
||||
setProxListening(false); // Don't immediately start listening when we register.
|
||||
mProximitySensor.register(
|
||||
proximityEvent -> {
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.display.AmbientDisplayConfiguration;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.text.format.Formatter;
|
||||
@@ -44,6 +43,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
import com.android.systemui.util.Assert;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
import com.android.systemui.util.sensors.ProximitySensor;
|
||||
import com.android.systemui.util.wakelock.WakeLock;
|
||||
@@ -156,10 +156,10 @@ public class DozeTriggers implements DozeMachine.Part {
|
||||
|
||||
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
|
||||
AlarmManager alarmManager, AmbientDisplayConfiguration config,
|
||||
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
|
||||
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
|
||||
ProximitySensor proximitySensor, DozeLog dozeLog,
|
||||
BroadcastDispatcher broadcastDispatcher) {
|
||||
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
|
||||
DelayableExecutor delayableExecutor, WakeLock wakeLock, boolean allowPulseTriggers,
|
||||
DockManager dockManager, ProximitySensor proximitySensor,
|
||||
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
|
||||
mContext = context;
|
||||
mMachine = machine;
|
||||
mDozeHost = dozeHost;
|
||||
@@ -169,11 +169,10 @@ public class DozeTriggers implements DozeMachine.Part {
|
||||
mWakeLock = wakeLock;
|
||||
mAllowPulseTriggers = allowPulseTriggers;
|
||||
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
|
||||
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog,
|
||||
proximitySensor);
|
||||
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
|
||||
mUiModeManager = mContext.getSystemService(UiModeManager.class);
|
||||
mDockManager = dockManager;
|
||||
mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
|
||||
mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, delayableExecutor);
|
||||
mDozeLog = dozeLog;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.systemui.util.sensors;
|
||||
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -27,6 +26,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -64,11 +64,12 @@ public class ProximitySensor implements ThresholdSensor {
|
||||
private final DelayableExecutor mDelayableExecutor;
|
||||
private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
|
||||
private String mTag = null;
|
||||
@VisibleForTesting protected boolean mPaused;
|
||||
private ThresholdSensorEvent mLastPrimaryEvent;
|
||||
@VisibleForTesting
|
||||
ThresholdSensorEvent mLastEvent;
|
||||
private boolean mPaused;
|
||||
private boolean mRegistered;
|
||||
private final AtomicBoolean mAlerting = new AtomicBoolean();
|
||||
private Runnable mCancelSecondaryRunnable;
|
||||
private boolean mInitializedListeners = false;
|
||||
|
||||
@@ -235,13 +236,16 @@ public class ProximitySensor implements ThresholdSensor {
|
||||
/** Update all listeners with the last value this class received from the sensor. */
|
||||
public void alertListeners() {
|
||||
Assert.isMainThread();
|
||||
if (mLastEvent == null) {
|
||||
if (mAlerting.getAndSet(true)) {
|
||||
return;
|
||||
}
|
||||
if (mLastEvent != null) {
|
||||
List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
|
||||
listeners.forEach(proximitySensorListener ->
|
||||
proximitySensorListener.onThresholdCrossed(mLastEvent));
|
||||
}
|
||||
|
||||
List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
|
||||
listeners.forEach(proximitySensorListener ->
|
||||
proximitySensorListener.onThresholdCrossed(mLastEvent));
|
||||
mAlerting.set(false);
|
||||
}
|
||||
|
||||
private void onPrimarySensorEvent(ThresholdSensorEvent event) {
|
||||
@@ -291,14 +295,14 @@ public class ProximitySensor implements ThresholdSensor {
|
||||
public static class ProximityCheck implements Runnable {
|
||||
|
||||
private final ProximitySensor mSensor;
|
||||
private final Handler mHandler;
|
||||
private final DelayableExecutor mDelayableExecutor;
|
||||
private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
public ProximityCheck(ProximitySensor sensor, Handler handler) {
|
||||
public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) {
|
||||
mSensor = sensor;
|
||||
mSensor.setTag("prox_check");
|
||||
mHandler = handler;
|
||||
mDelayableExecutor = delayableExecutor;
|
||||
mSensor.pause();
|
||||
ThresholdSensor.Listener listener = proximityEvent -> {
|
||||
mCallbacks.forEach(
|
||||
@@ -332,7 +336,7 @@ public class ProximitySensor implements ThresholdSensor {
|
||||
mCallbacks.add(callback);
|
||||
if (!mSensor.isRegistered()) {
|
||||
mSensor.resume();
|
||||
mHandler.postDelayed(this, timeoutMs);
|
||||
mDelayableExecutor.executeDelayed(this, timeoutMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.doze;
|
||||
|
||||
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -82,10 +83,7 @@ public class DozeSensorsTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private DozeLog mDozeLog;
|
||||
@Mock
|
||||
private Sensor mProximitySensor;
|
||||
@Mock
|
||||
private ProximitySensor mMockProxSensor;
|
||||
|
||||
private ProximitySensor mProximitySensor;
|
||||
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
|
||||
private TestableLooper mTestableLooper;
|
||||
private DozeSensors mDozeSensors;
|
||||
@@ -96,7 +94,6 @@ public class DozeSensorsTest extends SysuiTestCase {
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
|
||||
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
|
||||
when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProximitySensor);
|
||||
doAnswer(invocation -> {
|
||||
((Runnable) invocation.getArgument(0)).run();
|
||||
return null;
|
||||
@@ -106,10 +103,9 @@ public class DozeSensorsTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testRegisterProx() {
|
||||
// We should not register with the sensor manager initially.
|
||||
verify(mMockProxSensor).pause();
|
||||
assertFalse(mProximitySensor.isRegistered());
|
||||
mDozeSensors.setProxListening(true);
|
||||
verify(mMockProxSensor).resume();
|
||||
verify(mProximitySensor).resume();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -158,7 +154,7 @@ public class DozeSensorsTest extends SysuiTestCase {
|
||||
TestableDozeSensors() {
|
||||
super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
|
||||
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
|
||||
mMockProxSensor);
|
||||
mProximitySensor);
|
||||
for (TriggerSensor sensor : mSensors) {
|
||||
if (sensor instanceof PluginSensor
|
||||
&& ((PluginSensor) sensor).mPluginSensor.getType()
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.app.AlarmManager;
|
||||
import android.hardware.Sensor;
|
||||
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;
|
||||
@@ -98,7 +97,7 @@ public class DozeTriggersTest extends SysuiTestCase {
|
||||
mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor);
|
||||
|
||||
mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
|
||||
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
|
||||
asyncSensorManager, mExecutor, wakeLock, true,
|
||||
mDockManager, mProximitySensor, mock(DozeLog.class), mBroadcastDispatcher);
|
||||
waitForSensorManager();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
|
||||
public class FakeProximitySensor extends ProximitySensor {
|
||||
private boolean mAvailable;
|
||||
private boolean mRegistered;
|
||||
|
||||
public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
|
||||
DelayableExecutor delayableExecutor) {
|
||||
@@ -36,6 +37,11 @@ public class FakeProximitySensor extends ProximitySensor {
|
||||
mLastEvent = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered() {
|
||||
return mRegistered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return mAvailable;
|
||||
@@ -43,11 +49,11 @@ public class FakeProximitySensor extends ProximitySensor {
|
||||
|
||||
@Override
|
||||
protected void registerInternal() {
|
||||
// no-op
|
||||
mRegistered = !mPaused;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterInternal() {
|
||||
// no-op
|
||||
mRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.Assert;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class ProximityCheckTest extends SysuiTestCase {
|
||||
|
||||
private FakeProximitySensor mFakeProximitySensor;
|
||||
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
private TestableCallback mTestableCallback = new TestableCallback();
|
||||
|
||||
private ProximitySensor.ProximityCheck mProximityCheck;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Assert.setTestableLooper(TestableLooper.get(this).getLooper());
|
||||
FakeThresholdSensor thresholdSensor = new FakeThresholdSensor();
|
||||
thresholdSensor.setLoaded(true);
|
||||
mFakeProximitySensor = new FakeProximitySensor(thresholdSensor, null, mFakeExecutor);
|
||||
|
||||
mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheck() {
|
||||
mProximityCheck.check(100, mTestableCallback);
|
||||
|
||||
assertNull(mTestableCallback.mLastResult);
|
||||
|
||||
mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
|
||||
mFakeProximitySensor.alertListeners();
|
||||
|
||||
assertTrue(mTestableCallback.mLastResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout() {
|
||||
mProximityCheck.check(100, mTestableCallback);
|
||||
|
||||
assertTrue(mFakeProximitySensor.isRegistered());
|
||||
|
||||
mFakeExecutor.advanceClockToNext();
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
assertFalse(mFakeProximitySensor.isRegistered());
|
||||
}
|
||||
|
||||
private static class TestableCallback implements Consumer<Boolean> {
|
||||
Boolean mLastResult;
|
||||
@Override
|
||||
public void accept(Boolean result) {
|
||||
mLastResult = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,25 @@ public class ProximitySensorSingleTest extends SysuiTestCase {
|
||||
mProximitySensor.unregister(listenerB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreventRecursiveAlert() {
|
||||
TestableListener listenerA = new TestableListener() {
|
||||
@Override
|
||||
public void onThresholdCrossed(ProximitySensor.ThresholdSensorEvent proximityEvent) {
|
||||
super.onThresholdCrossed(proximityEvent);
|
||||
if (mCallCount < 2) {
|
||||
mProximitySensor.alertListeners();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mProximitySensor.register(listenerA);
|
||||
|
||||
mThresholdSensor.triggerEvent(true, 0);
|
||||
|
||||
assertEquals(1, listenerA.mCallCount);
|
||||
}
|
||||
|
||||
private static class TestableListener implements ThresholdSensor.Listener {
|
||||
ThresholdSensor.ThresholdSensorEvent mLastEvent;
|
||||
int mCallCount = 0;
|
||||
|
||||
Reference in New Issue
Block a user