Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9623a2c8 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge "Move pointer icon caching logic into a new class: PointerIconCache" into main

parents 566a74f7 d636009b
Loading
Loading
Loading
Loading
+8 −132
Original line number Diff line number Diff line
@@ -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 {
+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;
    }
}