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

Commit 6c07572f authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Deprecate InputMethodManager#getInstance()

With this CL, no one in the Framework is using
InputMethodManager#getInstance() directly or indirectly.  It is time
to mark this method deprecated.

For applications that still call InputMethodManager#getInstance()
directly or indirectly via reflection, they will start seeing warnings
with stacktrace in logcat.

Except for that explict warnings in logcat, there is no behavior
change in this CL.  Added a new test to make sure that
InputMethodManager#getInstance() and InputMethodManager#peekInstance()
are still working in a way we expected for such applications.

Fix: 115891476
Test: atest FrameworksCoreTests:android.view.inputmethod.InputMethodManagerTest
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Change-Id: Ib393086d921f91993395b5f0007b725a5db7bf22
parent daf0d1b6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -378,7 +378,7 @@ final class SystemServiceRegistry {
                new StaticServiceFetcher<InputMethodManager>() {
            @Override
            public InputMethodManager createService() {
                return InputMethodManager.getInstance();
                return InputMethodManager.getInstanceInternal();
            }});

        registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class,
+4 −5
Original line number Diff line number Diff line
@@ -196,11 +196,10 @@ public final class WindowManagerGlobal {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    if (InputMethodManager.ENABLE_LEGACY_EAGER_INITIALIZATION) {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                        InputMethodManager.getInstance();
                    }
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
+45 −12
Original line number Diff line number Diff line
@@ -228,12 +228,10 @@ public final class InputMethodManager {
    static final String PENDING_EVENT_COUNTER = "aq:imm";

    /**
     * {@code true} if we want to instantiate {@link InputMethodManager} eagerly in
     * {@link android.view.WindowManagerGlobal#getWindowSession()}, which is often called in an
     * early stage of process startup, which is how Android has worked.
     * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
     * or indirectly relied on {@link #sInstance} via reflection or something like that.
     *
     * <p>We still have this settings because we know there are possible compatibility concerns if
     * we stop doing so. Here are scenarios we know and there could be more scenarios we are not
     * <p>Here are scenarios we know and there could be more scenarios we are not
     * aware of right know.</p>
     *
     * <ul>
@@ -260,13 +258,22 @@ public final class InputMethodManager {
     *     {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li>
     * </ul>
     *
     * <p>TODO(Bug 116157766): Check if we can set {@code false} here then remove this settings.</p>
     * <p>Since this is purely a compatibility hack, this method must be used only from
     * {@link android.view.WindowManagerGlobal#getWindowSession()} and {@link #getInstance()}.</p>
     *
     * <p>TODO(Bug 116157766): Remove this method once we clean up {@link UnsupportedAppUsage}.</p>
     * @hide
     */
    public static final boolean ENABLE_LEGACY_EAGER_INITIALIZATION = true;
    public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {
        getInstanceInternal();
    }

    private static final Object sLock = new Object();

    /**
     * @deprecated This cannot be compatible with multi-display. Please do not use this.
     */
    @Deprecated
    @GuardedBy("sLock")
    @UnsupportedAppUsage
    static InputMethodManager sInstance;
@@ -735,12 +742,13 @@ public final class InputMethodManager {
    }

    /**
     * Retrieve the global InputMethodManager instance, creating it if it
     * doesn't already exist.
     * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already
     * exist.
     *
     * @return global {@link InputMethodManager} instance
     * @hide
     */
    @UnsupportedAppUsage
    public static InputMethodManager getInstance() {
    public static InputMethodManager getInstanceInternal() {
        synchronized (sLock) {
            if (sInstance == null) {
                try {
@@ -756,14 +764,39 @@ public final class InputMethodManager {
    }

    /**
     * Private optimization: retrieve the global InputMethodManager instance, if it exists.
     * Deprecated. Do not use.
     *
     * @return global {@link InputMethodManager} instance
     * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
     *             support multi-display scenario.
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public static InputMethodManager getInstance() {
        Log.w(TAG, "InputMethodManager.getInstance() is deprecated because it cannot be"
                        + " compatible with multi-display."
                        + " Use context.getSystemService(InputMethodManager.class) instead.",
                new Throwable());
        ensureDefaultInstanceForDefaultDisplayIfNecessary();
        return peekInstance();
    }

    /**
     * Deprecated. Do not use.
     *
     * @return {@link #sInstance}
     * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
     *             support multi-display scenario.
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public static InputMethodManager peekInstance() {
        Log.w(TAG, "InputMethodManager.peekInstance() is deprecated because it cannot be"
                        + " compatible with multi-display."
                        + " Use context.getSystemService(InputMethodManager.class) instead.",
                new Throwable());
        synchronized (sLock) {
            return sInstance;
        }
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.view.inputmethod;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.WindowManager;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodManagerTest {
    @Test
    public void testPrivateApiGetInstance() throws Exception {
        final InputMethodManager globalImm = InputMethodManager.getInstance();
        assertNotNull("InputMethodManager.getInstance() still needs to work due to"
                + " @UnsupportedAppUsage", globalImm);
        assertEquals("InputMethodManager.peekInstance() still needs to work due to"
                + " @UnsupportedAppUsage", globalImm, InputMethodManager.peekInstance());

        final Context testContext = InstrumentationRegistry.getInstrumentation()
                .getTargetContext();

        final WindowManager wm = testContext.getSystemService(WindowManager.class);
        final Context defaultDisplayContext =
                testContext.createDisplayContext(wm.getDefaultDisplay());
        final InputMethodManager imm =
                defaultDisplayContext.getSystemService(InputMethodManager.class);
        assertEquals("InputMethodManager.getInstance() always returns the instance for the default"
                + " display.", globalImm, imm);
    }
}