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:
Dave Mankoff
2020-05-13 12:46:18 -04:00
parent 6f718bba6f
commit 209e793a0b
9 changed files with 156 additions and 37 deletions

View File

@@ -34,6 +34,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.BatteryController; 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.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -56,6 +57,7 @@ public class DozeFactory {
private final ProximitySensor mProximitySensor; private final ProximitySensor mProximitySensor;
private final DelayedWakeLock.Builder mDelayedWakeLockBuilder; private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
private final Handler mHandler; private final Handler mHandler;
private final DelayableExecutor mDelayableExecutor;
private final BiometricUnlockController mBiometricUnlockController; private final BiometricUnlockController mBiometricUnlockController;
private final BroadcastDispatcher mBroadcastDispatcher; private final BroadcastDispatcher mBroadcastDispatcher;
private final DozeHost mDozeHost; private final DozeHost mDozeHost;
@@ -68,6 +70,7 @@ public class DozeFactory {
DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
ProximitySensor proximitySensor, ProximitySensor proximitySensor,
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
DelayableExecutor delayableExecutor,
BiometricUnlockController biometricUnlockController, BiometricUnlockController biometricUnlockController,
BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) { BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
mFalsingManager = falsingManager; mFalsingManager = falsingManager;
@@ -83,6 +86,7 @@ public class DozeFactory {
mProximitySensor = proximitySensor; mProximitySensor = proximitySensor;
mDelayedWakeLockBuilder = delayedWakeLockBuilder; mDelayedWakeLockBuilder = delayedWakeLockBuilder;
mHandler = handler; mHandler = handler;
mDelayableExecutor = delayableExecutor;
mBiometricUnlockController = biometricUnlockController; mBiometricUnlockController = biometricUnlockController;
mBroadcastDispatcher = broadcastDispatcher; mBroadcastDispatcher = broadcastDispatcher;
mDozeHost = dozeHost; mDozeHost = dozeHost;
@@ -107,8 +111,8 @@ public class DozeFactory {
new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()), new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
new DozeFalsingManagerAdapter(mFalsingManager), new DozeFalsingManagerAdapter(mFalsingManager),
createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost, createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost,
mAlarmManager, config, mDozeParameters, mHandler, wakeLock, machine, mAlarmManager, config, mDozeParameters, mDelayableExecutor, wakeLock,
mDockManager, mDozeLog), machine, mDockManager, mDozeLog),
createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler, createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler,
mAlarmManager, mDozeParameters, mDozeLog), mAlarmManager, mDozeParameters, mDozeLog),
new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters, new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters,
@@ -135,11 +139,11 @@ public class DozeFactory {
private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager, private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine, DozeParameters params, DelayableExecutor delayableExecutor, WakeLock wakeLock,
DockManager dockManager, DozeLog dozeLog) { DozeMachine machine, DockManager dockManager, DozeLog dozeLog) {
boolean allowPulseTriggers = true; boolean allowPulseTriggers = true;
return new DozeTriggers(context, machine, host, alarmManager, config, params, return new DozeTriggers(context, machine, host, alarmManager, config, params,
sensorManager, handler, wakeLock, allowPulseTriggers, dockManager, sensorManager, delayableExecutor, wakeLock, allowPulseTriggers, dockManager,
mProximitySensor, dozeLog, mBroadcastDispatcher); mProximitySensor, dozeLog, mBroadcastDispatcher);
} }

View File

@@ -110,6 +110,7 @@ public class DozeSensors {
mProxCallback = proxCallback; mProxCallback = proxCallback;
mResolver = mContext.getContentResolver(); mResolver = mContext.getContentResolver();
mCallback = callback; mCallback = callback;
mProximitySensor = proximitySensor;
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT); boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] { mSensors = new TriggerSensor[] {
@@ -172,7 +173,6 @@ public class DozeSensors {
dozeLog), dozeLog),
}; };
mProximitySensor = proximitySensor;
setProxListening(false); // Don't immediately start listening when we register. setProxListening(false); // Don't immediately start listening when we register.
mProximitySensor.register( mProximitySensor.register(
proximityEvent -> { proximityEvent -> {

View File

@@ -26,7 +26,6 @@ import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.AmbientDisplayConfiguration;
import android.metrics.LogMaker; import android.metrics.LogMaker;
import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.format.Formatter; 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.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert; 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.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock; 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, public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler, DozeParameters dozeParameters, AsyncSensorManager sensorManager,
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, DelayableExecutor delayableExecutor, WakeLock wakeLock, boolean allowPulseTriggers,
ProximitySensor proximitySensor, DozeLog dozeLog, DockManager dockManager, ProximitySensor proximitySensor,
BroadcastDispatcher broadcastDispatcher) { DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
mContext = context; mContext = context;
mMachine = machine; mMachine = machine;
mDozeHost = dozeHost; mDozeHost = dozeHost;
@@ -169,11 +169,10 @@ public class DozeTriggers implements DozeMachine.Part {
mWakeLock = wakeLock; mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers; mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
proximitySensor);
mUiModeManager = mContext.getSystemService(UiModeManager.class); mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager; mDockManager = dockManager;
mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler); mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, delayableExecutor);
mDozeLog = dozeLog; mDozeLog = dozeLog;
mBroadcastDispatcher = broadcastDispatcher; mBroadcastDispatcher = broadcastDispatcher;
} }

View File

@@ -17,7 +17,6 @@
package com.android.systemui.util.sensors; package com.android.systemui.util.sensors;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -27,6 +26,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.inject.Inject; import javax.inject.Inject;
@@ -64,11 +64,12 @@ public class ProximitySensor implements ThresholdSensor {
private final DelayableExecutor mDelayableExecutor; private final DelayableExecutor mDelayableExecutor;
private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>(); private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
private String mTag = null; private String mTag = null;
@VisibleForTesting protected boolean mPaused;
private ThresholdSensorEvent mLastPrimaryEvent; private ThresholdSensorEvent mLastPrimaryEvent;
@VisibleForTesting @VisibleForTesting
ThresholdSensorEvent mLastEvent; ThresholdSensorEvent mLastEvent;
private boolean mPaused;
private boolean mRegistered; private boolean mRegistered;
private final AtomicBoolean mAlerting = new AtomicBoolean();
private Runnable mCancelSecondaryRunnable; private Runnable mCancelSecondaryRunnable;
private boolean mInitializedListeners = false; private boolean mInitializedListeners = false;
@@ -235,15 +236,18 @@ public class ProximitySensor implements ThresholdSensor {
/** Update all listeners with the last value this class received from the sensor. */ /** Update all listeners with the last value this class received from the sensor. */
public void alertListeners() { public void alertListeners() {
Assert.isMainThread(); Assert.isMainThread();
if (mLastEvent == null) { if (mAlerting.getAndSet(true)) {
return; return;
} }
if (mLastEvent != null) {
List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners); List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
listeners.forEach(proximitySensorListener -> listeners.forEach(proximitySensorListener ->
proximitySensorListener.onThresholdCrossed(mLastEvent)); proximitySensorListener.onThresholdCrossed(mLastEvent));
} }
mAlerting.set(false);
}
private void onPrimarySensorEvent(ThresholdSensorEvent event) { private void onPrimarySensorEvent(ThresholdSensorEvent event) {
Assert.isMainThread(); Assert.isMainThread();
if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) { if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
@@ -291,14 +295,14 @@ public class ProximitySensor implements ThresholdSensor {
public static class ProximityCheck implements Runnable { public static class ProximityCheck implements Runnable {
private final ProximitySensor mSensor; private final ProximitySensor mSensor;
private final Handler mHandler; private final DelayableExecutor mDelayableExecutor;
private List<Consumer<Boolean>> mCallbacks = new ArrayList<>(); private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
@Inject @Inject
public ProximityCheck(ProximitySensor sensor, Handler handler) { public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) {
mSensor = sensor; mSensor = sensor;
mSensor.setTag("prox_check"); mSensor.setTag("prox_check");
mHandler = handler; mDelayableExecutor = delayableExecutor;
mSensor.pause(); mSensor.pause();
ThresholdSensor.Listener listener = proximityEvent -> { ThresholdSensor.Listener listener = proximityEvent -> {
mCallbacks.forEach( mCallbacks.forEach(
@@ -332,7 +336,7 @@ public class ProximitySensor implements ThresholdSensor {
mCallbacks.add(callback); mCallbacks.add(callback);
if (!mSensor.isRegistered()) { if (!mSensor.isRegistered()) {
mSensor.resume(); mSensor.resume();
mHandler.postDelayed(this, timeoutMs); mDelayableExecutor.executeDelayed(this, timeoutMs);
} }
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.systemui.doze;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; 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.any;
import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
@@ -82,10 +83,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Mock @Mock
private DozeLog mDozeLog; private DozeLog mDozeLog;
@Mock @Mock
private Sensor mProximitySensor; private ProximitySensor mProximitySensor;
@Mock
private ProximitySensor mMockProxSensor;
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors; private DozeSensors mDozeSensors;
@@ -96,7 +94,6 @@ public class DozeSensorsTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this); mTestableLooper = TestableLooper.get(this);
when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L); when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProximitySensor);
doAnswer(invocation -> { doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run(); ((Runnable) invocation.getArgument(0)).run();
return null; return null;
@@ -106,10 +103,9 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test @Test
public void testRegisterProx() { public void testRegisterProx() {
// We should not register with the sensor manager initially. assertFalse(mProximitySensor.isRegistered());
verify(mMockProxSensor).pause();
mDozeSensors.setProxListening(true); mDozeSensors.setProxListening(true);
verify(mMockProxSensor).resume(); verify(mProximitySensor).resume();
} }
@Test @Test
@@ -158,7 +154,7 @@ public class DozeSensorsTest extends SysuiTestCase {
TestableDozeSensors() { TestableDozeSensors() {
super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog, mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mMockProxSensor); mProximitySensor);
for (TriggerSensor sensor : mSensors) { for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType() && ((PluginSensor) sensor).mPluginSensor.getType()

View File

@@ -31,7 +31,6 @@ import android.app.AlarmManager;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper; import android.testing.TestableLooper.RunWithLooper;
@@ -98,7 +97,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor); mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor);
mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters, 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); mDockManager, mProximitySensor, mock(DozeLog.class), mBroadcastDispatcher);
waitForSensorManager(); waitForSensorManager();
} }

View File

@@ -20,6 +20,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
public class FakeProximitySensor extends ProximitySensor { public class FakeProximitySensor extends ProximitySensor {
private boolean mAvailable; private boolean mAvailable;
private boolean mRegistered;
public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary, public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
DelayableExecutor delayableExecutor) { DelayableExecutor delayableExecutor) {
@@ -36,6 +37,11 @@ public class FakeProximitySensor extends ProximitySensor {
mLastEvent = event; mLastEvent = event;
} }
@Override
public boolean isRegistered() {
return mRegistered;
}
@Override @Override
public boolean isLoaded() { public boolean isLoaded() {
return mAvailable; return mAvailable;
@@ -43,11 +49,11 @@ public class FakeProximitySensor extends ProximitySensor {
@Override @Override
protected void registerInternal() { protected void registerInternal() {
// no-op mRegistered = !mPaused;
} }
@Override @Override
protected void unregisterInternal() { protected void unregisterInternal() {
// no-op mRegistered = false;
} }
} }

View File

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

View File

@@ -210,6 +210,25 @@ public class ProximitySensorSingleTest extends SysuiTestCase {
mProximitySensor.unregister(listenerB); 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 { private static class TestableListener implements ThresholdSensor.Listener {
ThresholdSensor.ThresholdSensorEvent mLastEvent; ThresholdSensor.ThresholdSensorEvent mLastEvent;
int mCallCount = 0; int mCallCount = 0;