Extract InputSettingsObserver from InputManagerService

The code to observe settings changes and pass them down the stack is
quite bulky and repetitive. Extracting it to a separate class makes
InputManagerService a little less unwieldy, and we can refactor it a bit
to use a map of observers to make the code less verbose.

Bug: 251196347
Test: atest inputflinger_tests
Test: change settings values and check the changes are applied
Change-Id: Ic94b311aed7281aedd86fc6dffbe56104cf90297
This commit is contained in:
Harry Cutts
2023-01-25 14:20:24 +00:00
parent 4ab33a9413
commit 16625d3f96
2 changed files with 186 additions and 231 deletions

View File

@@ -32,7 +32,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.graphics.PointF;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors;
@@ -78,8 +77,6 @@ import android.os.VibrationEffect;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -147,8 +144,6 @@ public class InputManagerService extends IInputManager.Stub
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
// Feature flag name for the deep press feature
private static final String DEEP_PRESS_ENABLED = "deep_press_enabled";
// Feature flag name for the strategy to be used in VelocityTracker
private static final String VELOCITYTRACKER_STRATEGY_PROPERTY = "velocitytracker_strategy";
@@ -307,6 +302,9 @@ public class InputManagerService extends IInputManager.Stub
@GuardedBy("mInputMonitors")
final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>();
// Watches for settings changes and updates the native side appropriately.
private final InputSettingsObserver mSettingsObserver;
// Manages Keyboard layouts for Physical keyboards
private final KeyboardLayoutManager mKeyboardLayoutManager;
@@ -428,6 +426,7 @@ public class InputManagerService extends IInputManager.Stub
mContext = injector.getContext();
mHandler = new InputManagerHandler(injector.getLooper());
mNative = injector.getNativeService(this);
mSettingsObserver = new InputSettingsObserver(mContext, mHandler, mNative);
mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore,
injector.getLooper());
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
@@ -493,39 +492,7 @@ public class InputManagerService extends IInputManager.Stub
// Add ourselves to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
registerMousePointerSpeedSettingObserver();
registerTouchpadPointerSpeedSettingObserver();
registerTouchpadNaturalScrollingEnabledObserver();
registerTouchpadTapToClickEnabledObserver();
registerTouchpadRightClickZoneEnabledObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
registerLongPressTimeoutObserver();
registerMaximumObscuringOpacityForTouchSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateMousePointerSpeedFromSettings();
updateTouchpadPointerSpeedFromSettings();
updateTouchpadNaturalScrollingEnabledFromSettings();
updateTouchpadTapToClickEnabledFromSettings();
updateTouchpadRightClickZoneEnabledFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("user switched");
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updateMousePointerSpeedFromSettings();
updateTouchpadPointerSpeedFromSettings();
updateTouchpadNaturalScrollingEnabledFromSettings();
updateTouchpadTapToClickEnabledFromSettings();
updateTouchpadRightClickZoneEnabledFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("just booted");
updateMaximumObscuringOpacityForTouchFromSettings();
mSettingsObserver.registerAndUpdate();
}
// TODO(BT) Pass in parameter for bluetooth system
@@ -1349,11 +1316,6 @@ public class InputManagerService extends IInputManager.Stub
setPointerSpeedUnchecked(speed);
}
private void updateMousePointerSpeedFromSettings() {
int speed = getMousePointerSpeedSetting();
setPointerSpeedUnchecked(speed);
}
private void setPointerSpeedUnchecked(int speed) {
speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
InputManager.MAX_POINTER_SPEED);
@@ -1370,194 +1332,6 @@ public class InputManagerService extends IInputManager.Stub
properties -> properties.pointerIconVisible = visible);
}
private void registerMousePointerSpeedSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateMousePointerSpeedFromSettings();
}
}, UserHandle.USER_ALL);
}
private int getMousePointerSpeedSetting() {
int speed = InputManager.DEFAULT_POINTER_SPEED;
try {
speed = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.POINTER_SPEED, UserHandle.USER_CURRENT);
} catch (SettingNotFoundException ignored) {
}
return speed;
}
private void registerTouchpadPointerSpeedSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateTouchpadPointerSpeedFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateTouchpadPointerSpeedFromSettings() {
int speed = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.TOUCHPAD_POINTER_SPEED, InputManager.DEFAULT_POINTER_SPEED,
UserHandle.USER_CURRENT);
speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
InputManager.MAX_POINTER_SPEED);
mNative.setTouchpadPointerSpeed(speed);
}
private void registerTouchpadNaturalScrollingEnabledObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateTouchpadNaturalScrollingEnabledFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateTouchpadNaturalScrollingEnabledFromSettings() {
int setting = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.TOUCHPAD_NATURAL_SCROLLING, 0, UserHandle.USER_CURRENT);
mNative.setTouchpadNaturalScrollingEnabled(setting != 0);
}
private void registerTouchpadTapToClickEnabledObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_TO_CLICK), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateTouchpadTapToClickEnabledFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateTouchpadTapToClickEnabledFromSettings() {
int setting = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT);
mNative.setTouchpadTapToClickEnabled(setting != 0);
}
private void registerTouchpadRightClickZoneEnabledObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateTouchpadRightClickZoneEnabledFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateTouchpadRightClickZoneEnabledFromSettings() {
int setting = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT);
mNative.setTouchpadRightClickZoneEnabled(setting != 0);
}
private void updateShowTouchesFromSettings() {
int setting = getShowTouchesSetting(0);
mNative.setShowTouches(setting != 0);
}
private void registerShowTouchesSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateShowTouchesFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateAccessibilityLargePointerFromSettings() {
final int accessibilityConfig = Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
0, UserHandle.USER_CURRENT);
PointerIcon.setUseLargeIcons(accessibilityConfig == 1);
mNative.reloadPointerIcons();
}
private void registerAccessibilityLargePointerSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateAccessibilityLargePointerFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateDeepPressStatusFromSettings(String reason) {
// Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value
final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT,
UserHandle.USER_CURRENT);
final boolean featureEnabledFlag =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DEEP_PRESS_ENABLED, true /* default */);
final boolean enabled =
featureEnabledFlag && timeout <= ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
Log.i(TAG,
(enabled ? "Enabling" : "Disabling") + " motion classifier because " + reason
+ ": feature " + (featureEnabledFlag ? "enabled" : "disabled")
+ ", long press timeout = " + timeout);
mNative.setMotionClassifierEnabled(enabled);
}
private void registerLongPressTimeoutObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LONG_PRESS_TIMEOUT), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateDeepPressStatusFromSettings("timeout changed");
}
}, UserHandle.USER_ALL);
}
private void registerMaximumObscuringOpacityForTouchSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
/* notifyForDescendants */ true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateMaximumObscuringOpacityForTouchFromSettings();
}
}, UserHandle.USER_ALL);
}
private void updateMaximumObscuringOpacityForTouchFromSettings() {
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
final float opacity = im.getMaximumObscuringOpacityForTouch();
if (opacity < 0 || opacity > 1) {
Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+ ", it should be >= 0 and <= 1, rejecting update.");
return;
}
mNative.setMaximumObscuringOpacityForTouch(opacity);
}
private int getShowTouchesSetting(int defaultValue) {
int result = defaultValue;
try {
result = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.SHOW_TOUCHES, UserHandle.USER_CURRENT);
} catch (SettingNotFoundException snfe) {
}
return result;
}
/**
* Update the display on which the mouse pointer is shown.
*

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2023 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.server.input;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
import android.view.PointerIcon;
import android.view.ViewConfiguration;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/** Observes settings changes and propagates them to the native side. */
class InputSettingsObserver extends ContentObserver {
static final String TAG = "InputManager";
/** Feature flag name for the deep press feature */
private static final String DEEP_PRESS_ENABLED = "deep_press_enabled";
private final Context mContext;
private final Handler mHandler;
private final NativeInputManagerService mNative;
private final Map<Uri, Consumer<String /* reason*/>> mObservers;
InputSettingsObserver(Context context, Handler handler, NativeInputManagerService nativeIms) {
super(handler);
mContext = context;
mHandler = handler;
mNative = nativeIms;
mObservers = Map.ofEntries(
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SPEED),
(reason) -> updateMousePointerSpeed()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
(reason) -> updateTouchpadPointerSpeed()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
(reason) -> updateTouchpadNaturalScrollingEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_TO_CLICK),
(reason) -> updateTouchpadTapToClickEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE),
(reason) -> updateTouchpadRightClickZoneEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
(reason) -> updateShowTouches()),
Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON),
(reason) -> updateAccessibilityLargePointer()),
Map.entry(Settings.Secure.getUriFor(Settings.Secure.LONG_PRESS_TIMEOUT),
(reason) -> updateDeepPressStatus(reason)),
Map.entry(
Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
(reason) -> updateMaximumObscuringOpacityForTouch()));
}
/**
* Registers observers for input-related settings and updates the input subsystem with their
* current values.
*/
public void registerAndUpdate() {
for (Uri uri : mObservers.keySet()) {
mContext.getContentResolver().registerContentObserver(
uri, true /* notifyForDescendants */, this, UserHandle.USER_ALL);
}
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
for (Consumer<String> observer : mObservers.values()) {
observer.accept("user switched");
}
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
for (Consumer<String> observer : mObservers.values()) {
observer.accept("just booted");
}
}
@Override
public void onChange(boolean selfChange, Uri uri) {
mObservers.get(uri).accept("setting changed");
}
private boolean getBoolean(String settingName) {
final int setting = Settings.System.getIntForUser(mContext.getContentResolver(),
settingName, 0, UserHandle.USER_CURRENT);
return setting != 0;
}
private int getPointerSpeedValue(String settingName) {
int speed = Settings.System.getIntForUser(mContext.getContentResolver(),
settingName, InputManager.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT);
return Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
InputManager.MAX_POINTER_SPEED);
}
private void updateMousePointerSpeed() {
mNative.setPointerSpeed(getPointerSpeedValue(Settings.System.POINTER_SPEED));
}
private void updateTouchpadPointerSpeed() {
mNative.setTouchpadPointerSpeed(
getPointerSpeedValue(Settings.System.TOUCHPAD_POINTER_SPEED));
}
private void updateTouchpadNaturalScrollingEnabled() {
mNative.setTouchpadNaturalScrollingEnabled(
getBoolean(Settings.System.TOUCHPAD_NATURAL_SCROLLING));
}
private void updateTouchpadTapToClickEnabled() {
mNative.setTouchpadTapToClickEnabled(getBoolean(Settings.System.TOUCHPAD_TAP_TO_CLICK));
}
private void updateTouchpadRightClickZoneEnabled() {
mNative.setTouchpadRightClickZoneEnabled(
getBoolean(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE));
}
private void updateShowTouches() {
mNative.setShowTouches(getBoolean(Settings.System.SHOW_TOUCHES));
}
private void updateAccessibilityLargePointer() {
final int accessibilityConfig = Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
0, UserHandle.USER_CURRENT);
PointerIcon.setUseLargeIcons(accessibilityConfig == 1);
mNative.reloadPointerIcons();
}
private void updateDeepPressStatus(String reason) {
// Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value
final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT,
UserHandle.USER_CURRENT);
final boolean featureEnabledFlag =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DEEP_PRESS_ENABLED, true /* default */);
final boolean enabled =
featureEnabledFlag && timeout <= ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
Log.i(TAG,
(enabled ? "Enabling" : "Disabling") + " motion classifier because " + reason
+ ": feature " + (featureEnabledFlag ? "enabled" : "disabled")
+ ", long press timeout = " + timeout);
mNative.setMotionClassifierEnabled(enabled);
}
private void updateMaximumObscuringOpacityForTouch() {
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
final float opacity = im.getMaximumObscuringOpacityForTouch();
if (opacity < 0 || opacity > 1) {
Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+ ", it should be >= 0 and <= 1, rejecting update.");
return;
}
mNative.setMaximumObscuringOpacityForTouch(opacity);
}
}