diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e2caeec114d0a..84e63ec9cdb29 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -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 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. * diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java new file mode 100644 index 0000000000000..8ee3a721fc635 --- /dev/null +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -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> 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 observer : mObservers.values()) { + observer.accept("user switched"); + } + } + }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); + + for (Consumer 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); + } +}