Merge "Use Executor in AsyncSensorManager."

This commit is contained in:
Dave Mankoff
2020-07-09 00:13:22 +00:00
committed by Android (Google) Code Review
9 changed files with 177 additions and 45 deletions

View File

@@ -25,16 +25,21 @@ import androidx.annotation.VisibleForTesting;
*/
public class Assert {
private static final Looper sMainLooper = Looper.getMainLooper();
private static Looper sTestLooper = null;
private static Thread sTestThread = null;
@VisibleForTesting
public static void setTestableLooper(Looper testLooper) {
sTestLooper = testLooper;
setTestThread(testLooper == null ? null : testLooper.getThread());
}
@VisibleForTesting
public static void setTestThread(Thread thread) {
sTestThread = thread;
}
public static void isMainThread() {
if (!sMainLooper.isCurrentThread()
&& (sTestLooper == null || !sTestLooper.isCurrentThread())) {
&& (sTestThread == null || sTestThread != Thread.currentThread())) {
throw new IllegalStateException("should be called from the main thread."
+ " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+ " Thread.currentThread()=" + Thread.currentThread().getName());
@@ -43,7 +48,7 @@ public class Assert {
public static void isNotMainThread() {
if (sMainLooper.isCurrentThread()
&& (sTestLooper == null || sTestLooper.isCurrentThread())) {
&& (sTestThread == null || sTestThread == Thread.currentThread())) {
throw new IllegalStateException("should not be called from the main thread.");
}
}

View File

@@ -32,6 +32,7 @@ import java.util.concurrent.Executors;
import javax.inject.Singleton;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -199,4 +200,10 @@ public abstract class ConcurrencyModule {
public static Executor provideUiBackgroundExecutor() {
return Executors.newSingleThreadExecutor();
}
/**
* Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
*/
@Binds
public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
}

View File

@@ -0,0 +1,44 @@
/*
* 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.concurrency;
import java.util.concurrent.Executor;
/**
* Factory for building Executors running on a unique named thread.
*
* Use this when our generally available @Main, @Background, @UiBackground, @LongRunning, or
* similar global qualifiers don't quite cut it. Note that the methods here create entirely new
* threads; there are no singletons here. Use responsibly.
*/
public interface ThreadFactory {
/**
* Return an {@link java.util.concurrent.Executor} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
* implementation. Assume this is the case and use responsibly.
**/
Executor buildExecutorOnNewThread(String threadName);
/**
* Return an {@link DelayableExecutor} running on a named thread.
*
* The thread is implicitly started and may be left running indefinitely, depending on the
* implementation. Assume this is the case and use responsibly.
**/
DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
}

View File

@@ -0,0 +1,38 @@
/*
* 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.concurrency;
import android.os.HandlerThread;
import java.util.concurrent.Executor;
import javax.inject.Inject;
class ThreadFactoryImpl implements ThreadFactory {
@Inject
ThreadFactoryImpl() {}
public Executor buildExecutorOnNewThread(String threadName) {
return buildDelayableExecutorOnNewThread(threadName);
}
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
HandlerThread handlerThread = new HandlerThread(threadName);
handlerThread.start();
return new ExecutorImpl(handlerThread.getLooper());
}
}

View File

@@ -25,18 +25,18 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEventListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.MemoryFile;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.concurrency.ThreadFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -56,25 +56,14 @@ public class AsyncSensorManager extends SensorManager
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
private final Handler mHandler;
private final Executor mExecutor;
private final List<SensorManagerPlugin> mPlugins;
@Inject
public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
this(sensorManager, pluginManager, null);
}
@VisibleForTesting
public AsyncSensorManager(
SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
public AsyncSensorManager(SensorManager sensorManager, ThreadFactory threadFactory,
PluginManager pluginManager) {
mInner = sensorManager;
if (handler == null) {
HandlerThread handlerThread = new HandlerThread("async_sensor");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
} else {
mHandler = handler;
}
mExecutor = threadFactory.buildExecutorOnNewThread("async_sensor");
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
if (pluginManager != null) {
@@ -97,7 +86,7 @@ public class AsyncSensorManager extends SensorManager
protected boolean registerListenerImpl(SensorEventListener listener,
Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs,
int reservedFlags) {
mHandler.post(() -> {
mExecutor.execute(() -> {
if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
}
@@ -129,12 +118,12 @@ public class AsyncSensorManager extends SensorManager
@Override
protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
Handler handler) {
mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler));
mExecutor.execute(() -> mInner.registerDynamicSensorCallback(callback, handler));
}
@Override
protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) {
mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback));
mExecutor.execute(() -> mInner.unregisterDynamicSensorCallback(callback));
}
@Override
@@ -145,7 +134,7 @@ public class AsyncSensorManager extends SensorManager
if (sensor == null) {
throw new IllegalArgumentException("sensor cannot be null");
}
mHandler.post(() -> {
mExecutor.execute(() -> {
if (!mInner.requestTriggerSensor(listener, sensor)) {
Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
}
@@ -158,7 +147,7 @@ public class AsyncSensorManager extends SensorManager
boolean disable) {
Preconditions.checkArgument(disable);
mHandler.post(() -> {
mExecutor.execute(() -> {
if (!mInner.cancelTriggerSensor(listener, sensor)) {
Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed.");
}
@@ -178,7 +167,7 @@ public class AsyncSensorManager extends SensorManager
Log.w(TAG, "No plugins registered");
return false;
}
mHandler.post(() -> {
mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).registerListener(sensor, listener);
}
@@ -194,7 +183,7 @@ public class AsyncSensorManager extends SensorManager
*/
public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor,
SensorManagerPlugin.SensorEventListener listener) {
mHandler.post(() -> {
mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).unregisterListener(sensor, listener);
}
@@ -214,14 +203,14 @@ public class AsyncSensorManager extends SensorManager
@Override
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
mHandler.post(() -> mInner.setOperationParameter(parameter));
mExecutor.execute(() -> mInner.setOperationParameter(parameter));
return true;
}
@Override
protected void unregisterListenerImpl(SensorEventListener listener,
Sensor sensor) {
mHandler.post(() -> {
mExecutor.execute(() -> {
if (sensor == null) {
mInner.unregisterListener(listener);
} else {

View File

@@ -30,9 +30,7 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
@@ -43,6 +41,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.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
@@ -92,7 +91,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTapSensor = mSensors.getFakeTapSensor().getSensor();
WakeLock wakeLock = new WakeLockFake();
AsyncSensorManager asyncSensorManager =
new AsyncSensorManager(mSensors, null, new Handler());
new AsyncSensorManager(mSensors, new FakeThreadFactory(mExecutor), null);
FakeThresholdSensor thresholdSensor = new FakeThresholdSensor();
thresholdSensor.setLoaded(true);
@@ -186,6 +185,6 @@ public class DozeTriggersTest extends SysuiTestCase {
}
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
mExecutor.runAllReady();
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.concurrency;
import java.util.concurrent.Executor;
/**
* Implementation of {@link ThreadFactory} that returns {@link FakeExecutor} where it can.
*/
public class FakeThreadFactory implements ThreadFactory {
private final FakeExecutor mFakeExecutor;
public FakeThreadFactory(FakeExecutor fakeExecutor) {
mFakeExecutor = fakeExecutor;
}
@Override
public Executor buildExecutorOnNewThread(String threadName) {
return mFakeExecutor;
}
@Override
public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
return mFakeExecutor;
}
}

View File

@@ -23,15 +23,16 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
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.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -39,20 +40,20 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
mAsyncSensorManager = new AsyncSensorManager(
fakeSensorManager, mPluginManager, new Handler());
fakeSensorManager, new FakeThreadFactory(mFakeExecutor), mPluginManager);
mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -99,6 +100,6 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
}
public void waitUntilRequestsCompleted() {
TestableLooper.get(this).processAllMessages();
mFakeExecutor.runAllReady();
}
}

View File

@@ -20,12 +20,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -33,21 +35,20 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ThresholdSensorImplTest extends SysuiTestCase {
private ThresholdSensorImpl mThresholdSensor;
private FakeSensorManager mSensorManager;
private AsyncSensorManager mAsyncSensorManager;
private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
mSensorManager = new FakeSensorManager(getContext());
mAsyncSensorManager = new AsyncSensorManager(
mSensorManager, null, new Handler());
mSensorManager, new FakeThreadFactory(mFakeExecutor), null);
mFakeProximitySensor = mSensorManager.getFakeProximitySensor();
ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder(
@@ -60,6 +61,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testSingleListener() {
Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -81,6 +83,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testMultiListener() {
Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -114,6 +117,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testDuplicateListener() {
Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -138,6 +142,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
}
@Test
public void testUnregister() {
Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -157,6 +162,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testPauseAndResume() {
Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -199,6 +205,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testAlertListeners() {
Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -230,6 +237,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testHysteresis() {
Assert.setTestThread(Thread.currentThread());
float lowValue = 10f;
float highValue = 100f;
FakeSensorManager.FakeGenericSensor sensor = mSensorManager.getFakeLightSensor();
@@ -278,6 +286,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
@Test
public void testAlertAfterPause() {
Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
mThresholdSensor.register(listener);
@@ -307,7 +316,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
}
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
mFakeExecutor.runAllReady();
}
}