SysUI: Move all sensor operations to the background thread
Registering / unregistering listeners for sensors now frequently takes several hundred
miliseconds. Work around this by moving all sensor operations off the main thread.
Change-Id: Ic3537889e1d6e045149942bebb0211afe1192033
Fixes: 64103634
Test: Turn on phone from AOD2. Verify no jank.
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index e5c729f..9c1cb4e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -16,6 +16,7 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.hardware.SensorManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -84,6 +85,7 @@
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
@@ -156,6 +158,9 @@
mProviders.put(ActivityStarterDelegate.class, () ->
getDependency(ActivityStarter.class));
+ mProviders.put(AsyncSensorManager.class, () ->
+ new AsyncSensorManager(mContext.getSystemService(SensorManager.class)));
+
mProviders.put(BluetoothController.class, () ->
new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index e92ed2f..e4b405f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -35,6 +35,7 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.analytics.DataCollector;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.util.AsyncSensorManager;
import java.io.PrintWriter;
@@ -87,7 +88,7 @@
private FalsingManager(Context context) {
mContext = context;
- mSensorManager = mContext.getSystemService(SensorManager.class);
+ mSensorManager = Dependency.get(AsyncSensorManager.class);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mDataCollector = DataCollector.getInstance(mContext);
mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 91ca571..cbdabf5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -24,10 +24,12 @@
import android.os.Handler;
import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -39,7 +41,7 @@
/** Creates a DozeMachine with its parts for {@code dozeService}. */
public DozeMachine assembleMachine(DozeService dozeService) {
Context context = dozeService;
- SensorManager sensorManager = context.getSystemService(SensorManager.class);
+ SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
DozeHost host = getHost(dozeService);
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
new file mode 100644
index 0000000..a1cabff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.hardware.HardwareBuffer;
+import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
+import android.hardware.SensorDirectChannel;
+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.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Wrapper around sensor manager that hides potential sources of latency.
+ *
+ * Offloads fetching (non-dynamic) sensors and (un)registering listeners onto a background thread
+ * without blocking. Note that this means registering listeners now always appears successful even
+ * if it is not.
+ */
+public class AsyncSensorManager extends SensorManager {
+
+ private static final String TAG = "AsyncSensorManager";
+
+ private final SensorManager mInner;
+ private final List<Sensor> mSensorCache;
+ private final HandlerThread mHandlerThread = new HandlerThread("async_sensor");
+ private final Handler mHandler;
+
+ public AsyncSensorManager(SensorManager inner) {
+ mInner = inner;
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
+ }
+
+ @Override
+ protected List<Sensor> getFullSensorList() {
+ return mSensorCache;
+ }
+
+ @Override
+ protected List<Sensor> getFullDynamicSensorList() {
+ return mInner.getDynamicSensorList(Sensor.TYPE_ALL);
+ }
+
+ @Override
+ protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs,
+ Handler handler, int maxReportLatencyUs, int reservedFlags) {
+ mHandler.post(() -> {
+ if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
+ Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
+ }
+ });
+ return true;
+ }
+
+ @Override
+ protected boolean flushImpl(SensorEventListener listener) {
+ return mInner.flush(listener);
+ }
+
+ @Override
+ protected SensorDirectChannel createDirectChannelImpl(MemoryFile memoryFile,
+ HardwareBuffer hardwareBuffer) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void destroyDirectChannelImpl(SensorDirectChannel channel) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected int configureDirectChannelImpl(SensorDirectChannel channel, Sensor s, int rate) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
+ Handler handler) {
+ mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler));
+ }
+
+ @Override
+ protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) {
+ mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback));
+ }
+
+ @Override
+ protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+ mHandler.post(() -> {
+ if (!mInner.requestTriggerSensor(listener, sensor)) {
+ Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
+ }
+ });
+ return true;
+ }
+
+ @Override
+ protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
+ boolean disable) {
+ Preconditions.checkArgument(disable);
+
+ mHandler.post(() -> {
+ if (!mInner.cancelTriggerSensor(listener, sensor)) {
+ Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed.");
+ }
+ });
+ return true;
+ }
+
+ @Override
+ protected boolean initDataInjectionImpl(boolean enable) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
+ long timestamp) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+ mHandler.post(() -> mInner.setOperationParameter(parameter));
+ return true;
+ }
+
+ @Override
+ protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+ mHandler.post(() -> mInner.unregisterListener(listener, sensor));
+ }
+}