Loading services/core/java/com/android/server/input/InputManagerService.java +8 −132 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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() { Loading Loading @@ -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(); } Loading Loading @@ -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. Loading @@ -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, Loading Loading @@ -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 { Loading services/core/java/com/android/server/input/PointerIconCache.java 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * 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; } } Loading
services/core/java/com/android/server/input/InputManagerService.java +8 −132 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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() { Loading Loading @@ -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(); } Loading Loading @@ -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. Loading @@ -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, Loading Loading @@ -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 { Loading
services/core/java/com/android/server/input/PointerIconCache.java 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * 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; } }