diff options
| -rw-r--r-- | services/core/java/com/android/server/input/InputManagerService.java | 140 | ||||
| -rw-r--r-- | services/core/java/com/android/server/input/PointerIconCache.java | 207 | 
2 files changed, 215 insertions, 132 deletions
| diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index dbef427e23a7..a79e7715f064 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -38,7 +38,6 @@ import android.graphics.PointF;  import android.hardware.SensorPrivacyManager;  import android.hardware.SensorPrivacyManager.Sensors;  import android.hardware.SensorPrivacyManagerInternal; -import android.hardware.display.DisplayManager;  import android.hardware.display.DisplayManagerInternal;  import android.hardware.display.DisplayViewport;  import android.hardware.input.HostUsiVersion; @@ -86,9 +85,7 @@ import android.util.Log;  import android.util.Slog;  import android.util.SparseArray;  import android.util.SparseBooleanArray; -import android.util.SparseIntArray;  import android.view.Display; -import android.view.DisplayInfo;  import android.view.IInputFilter;  import android.view.IInputFilterHost;  import android.view.IInputMonitorHost; @@ -117,7 +114,6 @@ import com.android.internal.util.DumpUtils;  import com.android.internal.util.Preconditions;  import com.android.server.DisplayThread;  import com.android.server.LocalServices; -import com.android.server.UiThread;  import com.android.server.Watchdog;  import com.android.server.input.InputManagerInternal.LidSwitchCallback;  import com.android.server.input.debug.FocusEventDebugView; @@ -325,6 +321,9 @@ public class InputManagerService extends IInputManager.Stub      // Manages Keyboard modifier keys remapping      private final KeyRemapper mKeyRemapper; +    // Manages loading PointerIcons +    private final PointerIconCache mPointerIconCache; +      // Maximum number of milliseconds to wait for input event injection.      private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; @@ -411,74 +410,6 @@ public class InputManagerService extends IInputManager.Stub      private boolean mShowKeyPresses = false;      private boolean mShowRotaryInput = false; -    @GuardedBy("mLoadedPointerIconsByDisplayAndType") -    final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType = -            new SparseArray<>(); -    @GuardedBy("mLoadedPointerIconsByDisplayAndType") -    boolean mUseLargePointerIcons = false; -    @GuardedBy("mLoadedPointerIconsByDisplayAndType") -    final SparseArray<Context> mDisplayContexts = new SparseArray<>(); -    @GuardedBy("mLoadedPointerIconsByDisplayAndType") -    final SparseIntArray mDisplayDensities = new SparseIntArray(); - -    final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { -        @Override -        public void onDisplayAdded(int displayId) { -            synchronized (mLoadedPointerIconsByDisplayAndType) { -                updateDisplayDensity(displayId); -            } -        } - -        @Override -        public void onDisplayRemoved(int displayId) { -            synchronized (mLoadedPointerIconsByDisplayAndType) { -                mLoadedPointerIconsByDisplayAndType.remove(displayId); -                mDisplayContexts.remove(displayId); -                mDisplayDensities.delete(displayId); -            } -        } - -        @Override -        public void onDisplayChanged(int displayId) { -            synchronized (mLoadedPointerIconsByDisplayAndType) { -                if (!updateDisplayDensity(displayId)) { -                    return; -                } -                // The display density changed, so force all cached pointer icons to be -                // reloaded for the display. -                Slog.i(TAG, -                        "Reloading pointer icons due to density change on display: " + displayId); -                var iconsByType = mLoadedPointerIconsByDisplayAndType.get(displayId); -                if (iconsByType == null) { -                    return; -                } -                iconsByType.clear(); -                mDisplayContexts.remove(displayId); -            } -            mNative.reloadPointerIcons(); -        } - -        // Updates the cached display density for the given displayId, and returns true if -        // the cached density changed. -        @GuardedBy("mLoadedPointerIconsByDisplayAndType") -        private boolean updateDisplayDensity(int displayId) { -            final DisplayManager displayManager = Objects.requireNonNull( -                    mContext.getSystemService(DisplayManager.class)); -            final Display display = displayManager.getDisplay(displayId); -            if (display == null) { -                return false; -            } -            DisplayInfo info = new DisplayInfo(); -            display.getDisplayInfo(info); -            final int oldDensity = mDisplayDensities.get(displayId, 0 /* default */); -            if (oldDensity == info.logicalDensityDpi) { -                return false; -            } -            mDisplayDensities.put(displayId, info.logicalDensityDpi); -            return true; -        } -    }; -      /** Point of injection for test dependencies. */      @VisibleForTesting      static class Injector { @@ -537,6 +468,7 @@ public class InputManagerService extends IInputManager.Stub                  : new KeyboardBacklightControllerInterface() {};          mStickyModifierStateController = new StickyModifierStateController();          mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper()); +        mPointerIconCache = new PointerIconCache(mContext, mNative);          mUseDevInputEventForAudioJack =                  mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); @@ -646,18 +578,11 @@ public class InputManagerService extends IInputManager.Stub              mWiredAccessoryCallbacks.systemReady();          } -        final DisplayManager displayManager = Objects.requireNonNull( -                mContext.getSystemService(DisplayManager.class)); -        displayManager.registerDisplayListener(mDisplayListener, UiThread.getHandler()); -        final Display[] displays = displayManager.getDisplays(); -        for (int i = 0; i < displays.length; i++) { -            mDisplayListener.onDisplayAdded(displays[i].getDisplayId()); -        } -          mKeyboardLayoutManager.systemRunning();          mBatteryController.systemRunning();          mKeyboardBacklightController.systemRunning();          mKeyRemapper.systemRunning(); +        mPointerIconCache.systemRunning();      }      private void reloadDeviceAliases() { @@ -2411,8 +2336,8 @@ public class InputManagerService extends IInputManager.Stub          synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }          synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }          synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ } -        synchronized (mLoadedPointerIconsByDisplayAndType) { /* Test if blocked by pointer lock */}          mBatteryController.monitor(); +        mPointerIconCache.monitor();          mNative.monitor();      } @@ -2806,21 +2731,7 @@ public class InputManagerService extends IInputManager.Stub      // Native callback.      @SuppressWarnings("unused")      private @NonNull PointerIcon getLoadedPointerIcon(int displayId, int type) { -        synchronized (mLoadedPointerIconsByDisplayAndType) { -            SparseArray<PointerIcon> iconsByType = mLoadedPointerIconsByDisplayAndType.get( -                    displayId); -            if (iconsByType == null) { -                iconsByType = new SparseArray<>(); -                mLoadedPointerIconsByDisplayAndType.put(displayId, iconsByType); -            } -            PointerIcon icon = iconsByType.get(type); -            if (icon == null) { -                icon = PointerIcon.getLoadedSystemIcon(getContextForDisplay(displayId), type, -                        mUseLargePointerIcons); -                iconsByType.put(type, icon); -            } -            return Objects.requireNonNull(icon); -        } +        return mPointerIconCache.getLoadedPointerIcon(displayId, type);      }      // Native callback. @@ -2833,33 +2744,6 @@ public class InputManagerService extends IInputManager.Stub          return sc.mNativeObject;      } -    @GuardedBy("mLoadedPointerIconsByDisplayAndType") -    @NonNull -    private Context getContextForDisplay(int displayId) { -        if (displayId == Display.INVALID_DISPLAY) { -            // Fallback to using the default context. -            return mContext; -        } -        if (displayId == mContext.getDisplay().getDisplayId()) { -            return mContext; -        } - -        Context displayContext = mDisplayContexts.get(displayId); -        if (displayContext == null) { -            final DisplayManager displayManager = Objects.requireNonNull( -                    mContext.getSystemService(DisplayManager.class)); -            final Display display = displayManager.getDisplay(displayId); -            if (display == null) { -                // Fallback to using the default context. -                return mContext; -            } - -            displayContext = mContext.createDisplayContext(display); -            mDisplayContexts.put(displayId, displayContext); -        } -        return displayContext; -    } -      // Native callback.      @SuppressWarnings("unused")      private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier, String languageTag, @@ -3665,15 +3549,7 @@ public class InputManagerService extends IInputManager.Stub      }      void setUseLargePointerIcons(boolean useLargeIcons) { -        synchronized (mLoadedPointerIconsByDisplayAndType) { -            if (mUseLargePointerIcons == useLargeIcons) { -                return; -            } -            mUseLargePointerIcons = useLargeIcons; -            // Clear all cached icons on all displays. -            mLoadedPointerIconsByDisplayAndType.clear(); -        } -        UiThread.getHandler().post(mNative::reloadPointerIcons); +        mPointerIconCache.setUseLargePointerIcons(useLargeIcons);      }      interface KeyboardBacklightControllerInterface { diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java new file mode 100644 index 000000000000..233b865c69a6 --- /dev/null +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -0,0 +1,207 @@ +/* + * Copyright 2024 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.annotation.NonNull; +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.PointerIcon; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.UiThread; + +import java.util.Objects; + +/** + * A thread-safe component of {@link InputManagerService} responsible for caching loaded + * {@link PointerIcon}s and triggering reloading of the icons. + */ +final class PointerIconCache { +    private static final String TAG = PointerIconCache.class.getSimpleName(); + +    private final Context mContext; + +    // Do not hold the lock when calling into native code. +    private final NativeInputManagerService mNative; + +    // We use the UI thread for loading pointer icons. +    private final Handler mUiThreadHandler = UiThread.getHandler(); + +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType = +            new SparseArray<>(); +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private boolean mUseLargePointerIcons = false; +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private final SparseArray<Context> mDisplayContexts = new SparseArray<>(); +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private final SparseIntArray mDisplayDensities = new SparseIntArray(); + +    private final DisplayManager.DisplayListener mDisplayListener = +            new DisplayManager.DisplayListener() { +                @Override +                public void onDisplayAdded(int displayId) { +                    synchronized (mLoadedPointerIconsByDisplayAndType) { +                        updateDisplayDensityLocked(displayId); +                    } +                } + +                @Override +                public void onDisplayRemoved(int displayId) { +                    synchronized (mLoadedPointerIconsByDisplayAndType) { +                        mLoadedPointerIconsByDisplayAndType.remove(displayId); +                        mDisplayContexts.remove(displayId); +                        mDisplayDensities.delete(displayId); +                    } +                } + +                @Override +                public void onDisplayChanged(int displayId) { +                    handleDisplayChanged(displayId); +                } +            }; + +    /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) { +        mContext = context; +        mNative = nativeService; +    } + +    public void systemRunning() { +        final DisplayManager displayManager = Objects.requireNonNull( +                mContext.getSystemService(DisplayManager.class)); +        displayManager.registerDisplayListener(mDisplayListener, mUiThreadHandler); +        final Display[] displays = displayManager.getDisplays(); +        for (int i = 0; i < displays.length; i++) { +            mDisplayListener.onDisplayAdded(displays[i].getDisplayId()); +        } +    } + +    public void monitor() { +        synchronized (mLoadedPointerIconsByDisplayAndType) { /* Test if blocked by lock */} +    } + +    /** Set whether the large pointer icons should be used for accessibility. */ +    public void setUseLargePointerIcons(boolean useLargeIcons) { +        mUiThreadHandler.post(() -> handleSetUseLargePointerIcons(useLargeIcons)); +    } + +    /** +     * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if +     * it isn't already cached. +     */ +    public @NonNull PointerIcon getLoadedPointerIcon(int displayId, int type) { +        synchronized (mLoadedPointerIconsByDisplayAndType) { +            SparseArray<PointerIcon> iconsByType = mLoadedPointerIconsByDisplayAndType.get( +                    displayId); +            if (iconsByType == null) { +                iconsByType = new SparseArray<>(); +                mLoadedPointerIconsByDisplayAndType.put(displayId, iconsByType); +            } +            PointerIcon icon = iconsByType.get(type); +            if (icon == null) { +                icon = PointerIcon.getLoadedSystemIcon(getContextForDisplayLocked(displayId), type, +                        mUseLargePointerIcons); +                iconsByType.put(type, icon); +            } +            return Objects.requireNonNull(icon); +        } +    } + +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private @NonNull Context getContextForDisplayLocked(int displayId) { +        if (displayId == Display.INVALID_DISPLAY) { +            // Fallback to using the default context. +            return mContext; +        } +        if (displayId == mContext.getDisplay().getDisplayId()) { +            return mContext; +        } + +        Context displayContext = mDisplayContexts.get(displayId); +        if (displayContext == null) { +            final DisplayManager displayManager = Objects.requireNonNull( +                    mContext.getSystemService(DisplayManager.class)); +            final Display display = displayManager.getDisplay(displayId); +            if (display == null) { +                // Fallback to using the default context. +                return mContext; +            } + +            displayContext = mContext.createDisplayContext(display); +            mDisplayContexts.put(displayId, displayContext); +        } +        return displayContext; +    } + +    @android.annotation.UiThread +    private void handleDisplayChanged(int displayId) { +        synchronized (mLoadedPointerIconsByDisplayAndType) { +            if (!updateDisplayDensityLocked(displayId)) { +                return; +            } +            // The display density changed, so force all cached pointer icons to be +            // reloaded for the display. +            Slog.i(TAG, "Reloading pointer icons due to density change on display: " + displayId); +            var iconsByType = mLoadedPointerIconsByDisplayAndType.get(displayId); +            if (iconsByType == null) { +                return; +            } +            iconsByType.clear(); +            mDisplayContexts.remove(displayId); +        } +        mNative.reloadPointerIcons(); +    } + +    @android.annotation.UiThread +    private void handleSetUseLargePointerIcons(boolean useLargeIcons) { +        synchronized (mLoadedPointerIconsByDisplayAndType) { +            if (mUseLargePointerIcons == useLargeIcons) { +                return; +            } +            mUseLargePointerIcons = useLargeIcons; +            // Clear all cached icons on all displays. +            mLoadedPointerIconsByDisplayAndType.clear(); +        } +        mNative.reloadPointerIcons(); +    } + +    // Updates the cached display density for the given displayId, and returns true if +    // the cached density changed. +    @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private boolean updateDisplayDensityLocked(int displayId) { +        final DisplayManager displayManager = Objects.requireNonNull( +                mContext.getSystemService(DisplayManager.class)); +        final Display display = displayManager.getDisplay(displayId); +        if (display == null) { +            return false; +        } +        DisplayInfo info = new DisplayInfo(); +        display.getDisplayInfo(info); +        final int oldDensity = mDisplayDensities.get(displayId, 0 /* default */); +        if (oldDensity == info.logicalDensityDpi) { +            return false; +        } +        mDisplayDensities.put(displayId, info.logicalDensityDpi); +        return true; +    } +} |