diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 5e367046bd2b8..65729372363a5 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 8a23dcf25cd4c..59aba3edce013 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -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 -> { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 2ce66bb0e953f..d02cafbf277c0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -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; } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index a59d101c9f11b..7445aa91eaed3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -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 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 listeners = new ArrayList<>(mListeners); + listeners.forEach(proximitySensorListener -> + proximitySensorListener.onThresholdCrossed(mLastEvent)); + } - List 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> 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); } } } 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 539fa07c08fce..ebd2c3afe646a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -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() 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 eefd7249022c7..a55f03bdb6d4b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -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(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java index 16edc9d9cf82b..9bb4c4b084815 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java @@ -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; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java new file mode 100644 index 0000000000000..d83083bd12033 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -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 mLastResult; + @Override + public void accept(Boolean result) { + mLastResult = result; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java index f1cee5061695f..f2d5284d40093 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java @@ -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;