Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36d609f6 authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Use Executor in AsyncSensorManager.

This changes the Assert class to be able to work without a
TestableLooper being initialized in a test.

Bug: 159815966
Test: atest SystemUITests && manual
Change-Id: I5acaef842ee5ec643866fa51a8dfbc17eec45238
parent d8ad89c4
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -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.");
        }
    }
+7 −0
Original line number Diff line number Diff line
@@ -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);
}
+44 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+38 −0
Original line number Diff line number Diff line
/*
 * 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());
    }
}
+15 −26
Original line number Diff line number Diff line
@@ -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 {
Loading