1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.input; 18 19 import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; 20 import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; 21 import static android.view.flags.Flags.enableVectorCursorA11ySettings; 22 23 import static com.android.input.flags.Flags.rateLimitUserActivityPokeInDispatcher; 24 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.database.ContentObserver; 30 import android.hardware.input.InputSettings; 31 import android.net.Uri; 32 import android.os.Handler; 33 import android.os.UserHandle; 34 import android.provider.DeviceConfig; 35 import android.provider.Settings; 36 import android.util.Log; 37 import android.view.ViewConfiguration; 38 39 import java.util.Map; 40 import java.util.function.Consumer; 41 42 /** Observes settings changes and propagates them to the native side. */ 43 class InputSettingsObserver extends ContentObserver { 44 static final String TAG = "InputManager"; 45 46 /** Feature flag name for the deep press feature */ 47 private static final String DEEP_PRESS_ENABLED = "deep_press_enabled"; 48 49 private final Context mContext; 50 private final Handler mHandler; 51 private final InputManagerService mService; 52 private final NativeInputManagerService mNative; 53 private final Map<Uri, Consumer<String /* reason*/>> mObservers; 54 InputSettingsObserver(Context context, Handler handler, InputManagerService service, NativeInputManagerService nativeIms)55 InputSettingsObserver(Context context, Handler handler, InputManagerService service, 56 NativeInputManagerService nativeIms) { 57 super(handler); 58 mContext = context; 59 mHandler = handler; 60 mService = service; 61 mNative = nativeIms; 62 mObservers = Map.ofEntries( 63 Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SPEED), 64 (reason) -> updateMousePointerSpeed()), 65 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED), 66 (reason) -> updateTouchpadPointerSpeed()), 67 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING), 68 (reason) -> updateTouchpadNaturalScrollingEnabled()), 69 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_TO_CLICK), 70 (reason) -> updateTouchpadTapToClickEnabled()), 71 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_DRAGGING), 72 (reason) -> updateTouchpadTapDraggingEnabled()), 73 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE), 74 (reason) -> updateTouchpadRightClickZoneEnabled()), 75 Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), 76 (reason) -> updateShowTouches()), 77 Map.entry(Settings.System.getUriFor(Settings.System.POINTER_LOCATION), 78 (reason) -> updatePointerLocation()), 79 Map.entry( 80 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON), 81 (reason) -> updateAccessibilityLargePointer()), 82 Map.entry(Settings.Secure.getUriFor(Settings.Secure.LONG_PRESS_TIMEOUT), 83 (reason) -> updateLongPressTimeout(reason)), 84 Map.entry( 85 Settings.Global.getUriFor( 86 Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH), 87 (reason) -> updateMaximumObscuringOpacityForTouch()), 88 Map.entry(Settings.System.getUriFor(Settings.System.SHOW_KEY_PRESSES), 89 (reason) -> updateShowKeyPresses()), 90 Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_TIMEOUT_MS), 91 (reason) -> updateKeyRepeatInfo()), 92 Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_DELAY_MS), 93 (reason) -> updateKeyRepeatInfo()), 94 Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT), 95 (reason) -> updateShowRotaryInput()), 96 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS), 97 (reason) -> updateAccessibilityBounceKeys()), 98 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SLOW_KEYS), 99 (reason) -> updateAccessibilitySlowKeys()), 100 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS), 101 (reason) -> updateAccessibilityStickyKeys()), 102 Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED), 103 (reason) -> updateStylusPointerIconEnabled()), 104 Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE), 105 (reason) -> updatePointerFillStyleFromSettings()), 106 Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE), 107 (reason) -> updatePointerScaleFromSettings())); 108 } 109 110 /** 111 * Registers observers for input-related settings and updates the input subsystem with their 112 * current values. 113 */ registerAndUpdate()114 public void registerAndUpdate() { 115 for (Uri uri : mObservers.keySet()) { 116 mContext.getContentResolver().registerContentObserver( 117 uri, true /* notifyForDescendants */, this, UserHandle.USER_ALL); 118 } 119 120 mContext.registerReceiver(new BroadcastReceiver() { 121 @Override 122 public void onReceive(Context context, Intent intent) { 123 for (Consumer<String> observer : mObservers.values()) { 124 observer.accept("user switched"); 125 } 126 } 127 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); 128 129 for (Consumer<String> observer : mObservers.values()) { 130 observer.accept("just booted"); 131 } 132 133 configureUserActivityPokeInterval(); 134 } 135 136 @Override onChange(boolean selfChange, Uri uri)137 public void onChange(boolean selfChange, Uri uri) { 138 mObservers.get(uri).accept("setting changed"); 139 } 140 getBoolean(String settingName, boolean defaultValue)141 private boolean getBoolean(String settingName, boolean defaultValue) { 142 final int setting = Settings.System.getIntForUser(mContext.getContentResolver(), 143 settingName, defaultValue ? 1 : 0, UserHandle.USER_CURRENT); 144 return setting != 0; 145 } 146 constrainPointerSpeedValue(int speed)147 private int constrainPointerSpeedValue(int speed) { 148 return Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED), 149 InputSettings.MAX_POINTER_SPEED); 150 } 151 updateMousePointerSpeed()152 private void updateMousePointerSpeed() { 153 int speed = Settings.System.getIntForUser(mContext.getContentResolver(), 154 Settings.System.POINTER_SPEED, InputSettings.DEFAULT_POINTER_SPEED, 155 UserHandle.USER_CURRENT); 156 mNative.setPointerSpeed(constrainPointerSpeedValue(speed)); 157 } 158 updateTouchpadPointerSpeed()159 private void updateTouchpadPointerSpeed() { 160 mNative.setTouchpadPointerSpeed( 161 constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext))); 162 } 163 updateTouchpadNaturalScrollingEnabled()164 private void updateTouchpadNaturalScrollingEnabled() { 165 mNative.setTouchpadNaturalScrollingEnabled( 166 InputSettings.useTouchpadNaturalScrolling(mContext)); 167 } 168 updateTouchpadTapToClickEnabled()169 private void updateTouchpadTapToClickEnabled() { 170 mNative.setTouchpadTapToClickEnabled(InputSettings.useTouchpadTapToClick(mContext)); 171 } 172 updateTouchpadTapDraggingEnabled()173 private void updateTouchpadTapDraggingEnabled() { 174 mNative.setTouchpadTapDraggingEnabled(InputSettings.useTouchpadTapDragging(mContext)); 175 } 176 updateTouchpadRightClickZoneEnabled()177 private void updateTouchpadRightClickZoneEnabled() { 178 mNative.setTouchpadRightClickZoneEnabled(InputSettings.useTouchpadRightClickZone(mContext)); 179 } 180 updateShowTouches()181 private void updateShowTouches() { 182 mNative.setShowTouches(getBoolean(Settings.System.SHOW_TOUCHES, false)); 183 } 184 updatePointerLocation()185 private void updatePointerLocation() { 186 mService.updatePointerLocationEnabled( 187 getBoolean(Settings.System.POINTER_LOCATION, false)); 188 } 189 updateShowKeyPresses()190 private void updateShowKeyPresses() { 191 mService.updateShowKeyPresses(getBoolean(Settings.System.SHOW_KEY_PRESSES, false)); 192 } 193 updateShowRotaryInput()194 private void updateShowRotaryInput() { 195 mService.updateShowRotaryInput(getBoolean(Settings.System.SHOW_ROTARY_INPUT, false)); 196 } 197 updateAccessibilityLargePointer()198 private void updateAccessibilityLargePointer() { 199 final int accessibilityConfig = Settings.Secure.getIntForUser( 200 mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 201 0, UserHandle.USER_CURRENT); 202 mService.setUseLargePointerIcons(accessibilityConfig == 1); 203 } 204 updateLongPressTimeout(String reason)205 private void updateLongPressTimeout(String reason) { 206 // Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value. 207 final int longPressTimeoutMs = Settings.Secure.getIntForUser(mContext.getContentResolver(), 208 Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT, 209 UserHandle.USER_CURRENT); 210 211 final boolean featureEnabledFlag = 212 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, 213 DEEP_PRESS_ENABLED, true /* default */); 214 final boolean enabled = 215 featureEnabledFlag 216 && longPressTimeoutMs <= ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT; 217 Log.i(TAG, (enabled ? "Enabling" : "Disabling") + " motion classifier because " + reason 218 + ": feature " + (featureEnabledFlag ? "enabled" : "disabled") 219 + ", long press timeout = " + longPressTimeoutMs + " ms"); 220 mNative.setMotionClassifierEnabled(enabled); 221 } 222 updateKeyRepeatInfo()223 private void updateKeyRepeatInfo() { 224 // Use ViewConfiguration getters only as fallbacks because they may return stale values. 225 final int timeoutMs = Settings.Secure.getIntForUser(mContext.getContentResolver(), 226 Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(), 227 UserHandle.USER_CURRENT); 228 final int delayMs = Settings.Secure.getIntForUser(mContext.getContentResolver(), 229 Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(), 230 UserHandle.USER_CURRENT); 231 mNative.setKeyRepeatConfiguration(timeoutMs, delayMs); 232 } 233 updateMaximumObscuringOpacityForTouch()234 private void updateMaximumObscuringOpacityForTouch() { 235 final float opacity = InputSettings.getMaximumObscuringOpacityForTouch(mContext); 236 if (opacity < 0 || opacity > 1) { 237 Log.e(TAG, "Invalid maximum obscuring opacity " + opacity 238 + ", it should be >= 0 and <= 1, rejecting update."); 239 return; 240 } 241 mNative.setMaximumObscuringOpacityForTouch(opacity); 242 } 243 updateAccessibilityBounceKeys()244 private void updateAccessibilityBounceKeys() { 245 mService.setAccessibilityBounceKeysThreshold( 246 InputSettings.getAccessibilityBounceKeysThreshold(mContext)); 247 } 248 updateAccessibilitySlowKeys()249 private void updateAccessibilitySlowKeys() { 250 mService.setAccessibilitySlowKeysThreshold( 251 InputSettings.getAccessibilitySlowKeysThreshold(mContext)); 252 } 253 updateAccessibilityStickyKeys()254 private void updateAccessibilityStickyKeys() { 255 mService.setAccessibilityStickyKeysEnabled( 256 InputSettings.isAccessibilityStickyKeysEnabled(mContext)); 257 } 258 configureUserActivityPokeInterval()259 private void configureUserActivityPokeInterval() { 260 if (rateLimitUserActivityPokeInDispatcher()) { 261 final int intervalMillis = mContext.getResources().getInteger( 262 com.android.internal.R.integer.config_minMillisBetweenInputUserActivityEvents); 263 Log.i(TAG, "Setting user activity interval (ms) of " + intervalMillis); 264 mNative.setMinTimeBetweenUserActivityPokes(intervalMillis); 265 } 266 } 267 updateStylusPointerIconEnabled()268 private void updateStylusPointerIconEnabled() { 269 mNative.setStylusPointerIconEnabled( 270 InputSettings.isStylusPointerIconEnabled(mContext, true /* forceReloadSetting */)); 271 } 272 updatePointerFillStyleFromSettings()273 private void updatePointerFillStyleFromSettings() { 274 if (!enableVectorCursorA11ySettings()) { 275 return; 276 } 277 final int pointerFillStyle = Settings.System.getIntForUser( 278 mContext.getContentResolver(), Settings.System.POINTER_FILL_STYLE, 279 POINTER_ICON_VECTOR_STYLE_FILL_BLACK, 280 UserHandle.USER_CURRENT); 281 mService.setPointerFillStyle(pointerFillStyle); 282 } 283 updatePointerScaleFromSettings()284 private void updatePointerScaleFromSettings() { 285 if (!enableVectorCursorA11ySettings()) { 286 return; 287 } 288 final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(), 289 Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE, 290 UserHandle.USER_CURRENT); 291 mService.setPointerScale(pointerScale); 292 } 293 } 294