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

Commit fb12466e authored by Asmita Poddar's avatar Asmita Poddar
Browse files

Remove InputManager#getInstance

Replace all instances of InputManager with instances of InputManagerGlobal.
Add the context field in InputManager.
All methods previously accessed through instances of InputManager
have been refactored to be accessed through InputManagerGlobal, which is
a singleton class.
The SystemServiceRegistry no longer returns an instance of InputManager.

Bug: b/267758905
Test: Pre-submit
Change-Id: I2f3db1f5f4e27be9eb11d0d307aaee16a33f014d
parent 056b7183
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -506,10 +506,10 @@ public final class SystemServiceRegistry {

        // InputManager stores its own static instance for historical purposes.
        registerService(Context.INPUT_SERVICE, InputManager.class,
                new ServiceFetcher<InputManager>() {
                new CachedServiceFetcher<InputManager>() {
            @Override
            public InputManager getService(ContextImpl ctx) {
                return InputManager.getInstance(ctx.getOuterContext());
            public InputManager createService(ContextImpl ctx) {
                return new InputManager(ctx.getOuterContext());
            }});

        registerService(Context.DISPLAY_SERVICE, DisplayManager.class,
+20 −105
Original line number Diff line number Diff line
@@ -53,11 +53,8 @@ import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -73,19 +70,9 @@ public final class InputManager {
    // To enable these logs, run: 'adb shell setprop log.tag.InputManager DEBUG' (requires restart)
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private static InputManager sInstance;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private final IInputManager mIm;

    /**
     * We hold a weak reference to the context to avoid leaking it indefinitely,
     * since we currently store the input manager instance as a static variable that
     * will outlive any context.
     */
    @Nullable
    private WeakReference<Context> mWeakContext;

    /**
     * Whether a PointerIcon is shown for stylus pointers.
     * Obtain using {@link #isStylusPointerIconEnabled()}.
@@ -255,99 +242,43 @@ public final class InputManager {
     */
    public static final int SWITCH_STATE_ON = 1;

    private static String sVelocityTrackerStrategy;
    private final InputManagerGlobal mGlobal;
    private final Context mContext;

    private InputManagerGlobal mGlobal;

    private InputManager() {
    /** @hide */
    public InputManager(Context context) {
        mGlobal = InputManagerGlobal.getInstance();
        mIm = mGlobal.getInputManagerService();
        try {
            sVelocityTrackerStrategy = mIm.getVelocityTrackerStrategy();
        } catch (RemoteException ex) {
            Log.w(TAG, "Could not get VelocityTracker strategy: " + ex);
        }
        mContext = context;
    }

    /**
     * Gets an instance of the input manager.
     *
     * @return The input manager instance.
     *
     * @hide
     */
    @VisibleForTesting
    public static InputManager resetInstance(IInputManager inputManagerService) {
        synchronized (InputManager.class) {
            InputManagerGlobal.resetInstance(inputManagerService);
            sInstance = new InputManager();
            return sInstance;
        }
    }

    /**
     * Clear the instance of the input manager.
     *
     * @hide
     */
    @VisibleForTesting
    public static void clearInstance() {
        synchronized (InputManager.class) {
            InputManagerGlobal.clearInstance();
            sInstance = null;
        }
    }

    /**
     * Gets an instance of the input manager.
     *  Warning: The usage of this method is not supported!
     *
     *  @return The input manager instance.
     * @deprecated Use {@link Context#getSystemService(Class)} or {@link #getInstance(Context)}
     *  Use {@link Context#getSystemService(Class)}
     *  to obtain the InputManager instance.
     *
     * TODO (b/277717573): Soft remove this API in version V.
     * TODO (b/277039664): Migrate app usage off this API.
     *
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public static InputManager getInstance() {
        return getInstance(ActivityThread.currentApplication());
        return Objects.requireNonNull(ActivityThread.currentApplication())
                .getSystemService(InputManager.class);
    }

    /**
     * Gets an instance of the input manager.
     *
     * @return The input manager instance.
     * @hide
     */
    public static InputManager getInstance(Context context) {
        synchronized (InputManager.class) {
            if (sInstance == null) {
                sInstance = new InputManager();
            }
            if (sInstance.mWeakContext == null || sInstance.mWeakContext.get() == null) {
                sInstance.mWeakContext = new WeakReference(context);
            }
            return sInstance;
        }
    }

    @NonNull
    private Context getContext() {
        WeakReference<Context> weakContext = Objects.requireNonNull(mWeakContext,
                "A context is required for InputManager. Get the InputManager instance using "
                        + "Context#getSystemService before calling this method.");
        // If we get at this point, an app calling this function could potentially expect a
        // Context that has disappeared due to garbage collection. Holding a weak reference
        // is a temporary solution that should be resolved before the release of a
        // production version. This is being tracked in b/267758905
        return Objects.requireNonNull(weakContext.get(), "missing Context");
    }

    /**
     * Get the current VelocityTracker strategy. Only works when the system has fully booted up.
     * Get the current VelocityTracker strategy.
     * @hide
     */
    public String getVelocityTrackerStrategy() {
        return sVelocityTrackerStrategy;
        return mGlobal.getVelocityTrackerStrategy();
    }

    /**
@@ -584,11 +515,7 @@ public final class InputManager {
    @NonNull
    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
            @NonNull InputDeviceIdentifier identifier) {
        try {
            return mIm.getKeyboardLayoutsForInputDevice(identifier);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        return mGlobal.getKeyboardLayoutsForInputDevice(identifier);
    }

    /**
@@ -647,19 +574,8 @@ public final class InputManager {
    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
    public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
            @NonNull String keyboardLayoutDescriptor) {
        if (identifier == null) {
            throw new IllegalArgumentException("identifier must not be null");
        }
        if (keyboardLayoutDescriptor == null) {
            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
        }

        try {
            mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
        mGlobal.setCurrentKeyboardLayoutForInputDevice(identifier,
                keyboardLayoutDescriptor);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    /**
@@ -956,8 +872,7 @@ public final class InputManager {
     */
    @FloatRange(from = 0, to = 1)
    public float getMaximumObscuringOpacityForTouch() {
        Context context = ActivityThread.currentApplication();
        return InputSettings.getMaximumObscuringOpacityForTouch(context);
        return InputSettings.getMaximumObscuringOpacityForTouch(mContext);
    }

    /**
@@ -1123,7 +1038,7 @@ public final class InputManager {
     */
    public boolean isStylusPointerIconEnabled() {
        if (mIsStylusPointerIconEnabled == null) {
            mIsStylusPointerIconEnabled = getContext().getResources()
            mIsStylusPointerIconEnabled = mContext.getResources()
                    .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
                    || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
        }
+61 −19
Original line number Diff line number Diff line
@@ -102,17 +102,26 @@ public final class InputManagerGlobal {

    private static InputManagerGlobal sInstance;

    private final String mVelocityTrackerStrategy;

    private final IInputManager mIm;

    public InputManagerGlobal(IInputManager im) {
        mIm = im;
        String strategy = null;
        try {
            strategy = mIm.getVelocityTrackerStrategy();
        } catch (RemoteException ex) {
            Log.w(TAG, "Could not get VelocityTracker strategy: " + ex);
        }
        mVelocityTrackerStrategy = strategy;
    }

    /**
     * Gets an instance of the input manager global singleton.
     *
     * @return The display manager instance, may be null early in system startup
     * before the display manager has been fully initialized.
     * @return The input manager instance, may be null early in system startup
     * before the input manager has been fully initialized.
     */
    public static InputManagerGlobal getInstance() {
        synchronized (InputManagerGlobal.class) {
@@ -151,6 +160,14 @@ public final class InputManagerGlobal {
        }
    }

    /**
     * Get the current VelocityTracker strategy.
     * Only works when the system has fully booted up.
     */
    public String getVelocityTrackerStrategy() {
        return mVelocityTrackerStrategy;
    }

    /**
     * @see InputManager#getInputDevice(int)
     */
@@ -309,9 +326,7 @@ public final class InputManagerGlobal {
     * @see InputManager#registerInputDeviceListener
     */
    public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
        Objects.requireNonNull(listener, "listener must not be null");

        synchronized (mInputDeviceListeners) {
            populateInputDevicesLocked();
@@ -407,9 +422,7 @@ public final class InputManagerGlobal {
     * @see InputManager#getInputDeviceByDescriptor
     */
    InputDevice getInputDeviceByDescriptor(String descriptor) {
        if (descriptor == null) {
            throw new IllegalArgumentException("descriptor must not be null.");
        }
        Objects.requireNonNull(descriptor, "descriptor must not be null.");

        synchronized (mInputDeviceListeners) {
            populateInputDevicesLocked();
@@ -526,9 +539,8 @@ public final class InputManagerGlobal {
     */
    void registerOnTabletModeChangedListener(
            OnTabletModeChangedListener listener, Handler handler) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
        Objects.requireNonNull(listener, "listener must not be null");

        synchronized (mOnTabletModeChangedListeners) {
            if (mOnTabletModeChangedListeners == null) {
                initializeTabletModeListenerLocked();
@@ -546,9 +558,8 @@ public final class InputManagerGlobal {
     * @see InputManager#unregisterOnTabletModeChangedListener(OnTabletModeChangedListener)
     */
    void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
        Objects.requireNonNull(listener, "listener must not be null");

        synchronized (mOnTabletModeChangedListeners) {
            int idx = findOnTabletModeChangedListenerLocked(listener);
            if (idx >= 0) {
@@ -603,7 +614,7 @@ public final class InputManagerGlobal {
    /**
     * @see InputManager#addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener)
     */
    void addInputDeviceBatteryListener(int deviceId, @NonNull Executor executor,
    public void addInputDeviceBatteryListener(int deviceId, @NonNull Executor executor,
            @NonNull InputDeviceBatteryListener listener) {
        Objects.requireNonNull(executor, "executor should not be null");
        Objects.requireNonNull(listener, "listener should not be null");
@@ -714,7 +725,7 @@ public final class InputManagerGlobal {
    }

    /**
     * @see InputManager#getInputDeviceBatteryState(int, boolean)
     * @see #getInputDeviceBatteryState(int, boolean)
     */
    @NonNull
    public BatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
@@ -876,6 +887,38 @@ public final class InputManagerGlobal {
        }
    }

    /**
     * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier)
     */
    @NonNull
    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
            @NonNull InputDeviceIdentifier identifier) {
        try {
            return mIm.getKeyboardLayoutsForInputDevice(identifier);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    /**
     * @see InputManager#setCurrentKeyboardLayoutForInputDevice
     * (InputDeviceIdentifier, String)
     */
    @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
    public void setCurrentKeyboardLayoutForInputDevice(
            @NonNull InputDeviceIdentifier identifier,
            @NonNull String keyboardLayoutDescriptor) {
        Objects.requireNonNull(identifier, "identifier must not be null");
        Objects.requireNonNull(keyboardLayoutDescriptor,
                "keyboardLayoutDescriptor must not be null");
        try {
            mIm.setCurrentKeyboardLayoutForInputDevice(identifier,
                    keyboardLayoutDescriptor);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

    /**
     * @see InputDevice#getSensorManager()
     */
@@ -1162,9 +1205,8 @@ public final class InputManagerGlobal {
     */

    public boolean injectInputEvent(InputEvent event, int mode, int targetUid) {
        if (event == null) {
            throw new IllegalArgumentException("event must not be null");
        }
        Objects.requireNonNull(event , "event must not be null");

        if (mode != InputEventInjectionSync.NONE
                && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
                && mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
+3 −2
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ package android.view;

import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
import android.util.ArrayMap;
import android.util.Pools.SynchronizedPool;

@@ -286,7 +286,8 @@ public final class VelocityTracker {
    private VelocityTracker(@VelocityTrackerStrategy int strategy) {
        // If user has not selected a specific strategy
        if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
            final String strategyProperty = InputManager.getInstance().getVelocityTrackerStrategy();
            final String strategyProperty = InputManagerGlobal.getInstance()
                    .getVelocityTrackerStrategy();
            // Check if user specified strategy by overriding system property.
            if (strategyProperty == null || strategyProperty.isEmpty()) {
                mStrategy = strategy;
+12 −2
Original line number Diff line number Diff line
@@ -15,11 +15,14 @@
 */
package android.hardware.input

import android.content.Context
import android.content.ContextWrapper
import android.hardware.BatteryState
import android.os.Handler
import android.os.HandlerExecutor
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
import java.util.concurrent.Executor
import kotlin.test.assertEquals
@@ -32,8 +35,10 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoJUnitRunner

@@ -53,6 +58,7 @@ class InputDeviceBatteryListenerTest {
    private var registeredListener: IInputDeviceBatteryListener? = null
    private val monitoredDevices = mutableListOf<Int>()
    private lateinit var executor: Executor
    private lateinit var context: Context
    private lateinit var inputManager: InputManager

    @Mock
@@ -60,11 +66,15 @@ class InputDeviceBatteryListenerTest {

    @Before
    fun setUp() {
        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
        testLooper = TestLooper()
        executor = HandlerExecutor(Handler(testLooper.looper))
        registeredListener = null
        monitoredDevices.clear()
        inputManager = InputManager.resetInstance(iInputManagerMock)
        InputManagerGlobal.resetInstance(iInputManagerMock)
        inputManager = InputManager(context)
        `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                .thenReturn(inputManager)

        // Handle battery listener registration.
        doAnswer {
@@ -102,7 +112,7 @@ class InputDeviceBatteryListenerTest {

    @After
    fun tearDown() {
        InputManager.clearInstance()
        InputManagerGlobal.clearInstance()
    }

    private fun notifyBatteryStateChanged(
Loading