Move battery updates to a background HandlerThread

They can cause long Binder transactions that lead to ANRs.
This commit is contained in:
Cameron Gutman
2023-10-07 00:49:03 -05:00
parent bc27492206
commit d250f4dc60

View File

@@ -19,6 +19,7 @@ import android.media.AudioAttributes;
import android.os.Build; import android.os.Build;
import android.os.CombinedVibration; import android.os.CombinedVibration;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper; import android.os.Looper;
import android.os.VibrationAttributes; import android.os.VibrationAttributes;
import android.os.VibrationEffect; import android.os.VibrationEffect;
@@ -116,7 +117,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final VibratorManager deviceVibratorManager; private final VibratorManager deviceVibratorManager;
private final SensorManager deviceSensorManager; private final SensorManager deviceSensorManager;
private final SceManager sceManager; private final SceManager sceManager;
private final Handler handler; private final Handler mainThreadHandler;
private final HandlerThread backgroundHandlerThread;
private final Handler backgroundThreadHandler;
private boolean hasGameController; private boolean hasGameController;
private boolean stopped = false; private boolean stopped = false;
@@ -130,7 +133,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.prefConfig = prefConfig; this.prefConfig = prefConfig;
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE); this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
this.deviceSensorManager = (SensorManager) activityContext.getSystemService(Context.SENSOR_SERVICE); this.deviceSensorManager = (SensorManager) activityContext.getSystemService(Context.SENSOR_SERVICE);
this.handler = new Handler(Looper.getMainLooper()); this.mainThreadHandler = new Handler(Looper.getMainLooper());
// Create a HandlerThread to process battery state updates. These can be slow enough
// that they lead to ANRs if we do them on the main thread.
this.backgroundHandlerThread = new HandlerThread("ControllerHandler");
this.backgroundHandlerThread.start();
this.backgroundThreadHandler = new Handler(backgroundHandlerThread.getLooper());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
this.deviceVibratorManager = (VibratorManager) activityContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE); this.deviceVibratorManager = (VibratorManager) activityContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
@@ -272,6 +281,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
sceManager.stop(); sceManager.stop();
backgroundHandlerThread.quit();
} }
public void disableSensors() { public void disableSensors() {
@@ -982,6 +992,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
// This must not be called on the main thread due to risk of ANRs!
private void sendControllerBatteryPacket(InputDeviceContext context) { private void sendControllerBatteryPacket(InputDeviceContext context) {
int currentBatteryStatus; int currentBatteryStatus;
float currentBatteryCapacity; float currentBatteryCapacity;
@@ -2709,23 +2720,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
sendEmulatedMouseEvent(rightStickX, rightStickY); sendEmulatedMouseEvent(rightStickX, rightStickY);
// Requeue the callback // Requeue the callback
handler.postDelayed(this, mouseEmulationReportPeriod); mainThreadHandler.postDelayed(this, mouseEmulationReportPeriod);
} }
}; };
public void toggleMouseEmulation() { public void toggleMouseEmulation() {
handler.removeCallbacks(mouseEmulationRunnable); mainThreadHandler.removeCallbacks(mouseEmulationRunnable);
mouseEmulationActive = !mouseEmulationActive; mouseEmulationActive = !mouseEmulationActive;
Toast.makeText(activityContext, "Mouse emulation is: " + (mouseEmulationActive ? "ON" : "OFF"), Toast.LENGTH_SHORT).show(); Toast.makeText(activityContext, "Mouse emulation is: " + (mouseEmulationActive ? "ON" : "OFF"), Toast.LENGTH_SHORT).show();
if (mouseEmulationActive) { if (mouseEmulationActive) {
handler.postDelayed(mouseEmulationRunnable, mouseEmulationReportPeriod); mainThreadHandler.postDelayed(mouseEmulationRunnable, mouseEmulationReportPeriod);
} }
} }
public void destroy() { public void destroy() {
mouseEmulationActive = false; mouseEmulationActive = false;
handler.removeCallbacks(mouseEmulationRunnable); mainThreadHandler.removeCallbacks(mouseEmulationRunnable);
} }
public void sendControllerArrival() {} public void sendControllerArrival() {}
@@ -2805,7 +2816,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
sendControllerBatteryPacket(InputDeviceContext.this); sendControllerBatteryPacket(InputDeviceContext.this);
// Requeue the callback // Requeue the callback
handler.postDelayed(this, BATTERY_RECHECK_INTERVAL_MS); backgroundThreadHandler.postDelayed(this, BATTERY_RECHECK_INTERVAL_MS);
} }
}; };
@@ -2833,7 +2844,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
handler.removeCallbacks(batteryStateUpdateRunnable); backgroundThreadHandler.removeCallbacks(batteryStateUpdateRunnable);
} }
@Override @Override
@@ -2955,8 +2966,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
type, supportedButtonFlags, capabilities); type, supportedButtonFlags, capabilities);
// After reporting arrival to the host, send initial battery state and begin monitoring // After reporting arrival to the host, send initial battery state and begin monitoring
sendControllerBatteryPacket(this); backgroundThreadHandler.post(batteryStateUpdateRunnable);
handler.postDelayed(batteryStateUpdateRunnable, BATTERY_RECHECK_INTERVAL_MS);
} }
public void migrateContext(InputDeviceContext oldContext) { public void migrateContext(InputDeviceContext oldContext) {
@@ -2988,8 +2998,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
// Refresh battery state and start the battery state polling again // Refresh battery state and start the battery state polling again
sendControllerBatteryPacket(this); backgroundThreadHandler.post(batteryStateUpdateRunnable);
handler.postDelayed(batteryStateUpdateRunnable, BATTERY_RECHECK_INTERVAL_MS);
} }
public void disableSensors() { public void disableSensors() {