Merge changes from topic "ime_migration" into sc-v2-dev

* changes:
  Move IME log to WindowTokenClient
  Migrate InputMethodService to WindowProviderService
This commit is contained in:
Charles Chen
2021-06-21 08:46:33 +00:00
committed by Android (Google) Code Review
12 changed files with 124 additions and 60 deletions

View File

@@ -1270,6 +1270,12 @@ package android.hardware.soundtrigger {
package android.inputmethodservice { package android.inputmethodservice {
public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback {
method public final int getInitialDisplayId();
method @Nullable public final android.os.Bundle getWindowContextOptions();
method public final int getWindowType();
}
@UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService { @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
} }
@@ -3243,6 +3249,7 @@ package android.window {
@UiContext public abstract class WindowProviderService extends android.app.Service { @UiContext public abstract class WindowProviderService extends android.app.Service {
ctor public WindowProviderService(); ctor public WindowProviderService();
method public final void attachToWindowToken(@NonNull android.os.IBinder); method public final void attachToWindowToken(@NonNull android.os.IBinder);
method @NonNull public int getInitialDisplayId();
method @Nullable public android.os.Bundle getWindowContextOptions(); method @Nullable public android.os.Bundle getWindowContextOptions();
method public abstract int getWindowType(); method public abstract int getWindowType();
} }

View File

@@ -96,7 +96,6 @@ import android.graphics.Canvas;
import android.graphics.HardwareRenderer; import android.graphics.HardwareRenderer;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerGlobal;
import android.inputmethodservice.InputMethodService;
import android.media.MediaFrameworkInitializer; import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer; import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager; import android.media.MediaServiceManager;
@@ -5779,9 +5778,6 @@ public final class ActivityThread extends ClientTransactionHandler
final int NSVC = mServices.size(); final int NSVC = mServices.size();
for (int i=0; i<NSVC; i++) { for (int i=0; i<NSVC; i++) {
final Service service = mServices.valueAt(i); final Service service = mServices.valueAt(i);
if (service instanceof InputMethodService) {
mHasImeComponent = true;
}
// If {@code includeUiContext} is set to false, WindowProviderService should not be // If {@code includeUiContext} is set to false, WindowProviderService should not be
// collected because WindowProviderService is a UI Context. // collected because WindowProviderService is a UI Context.
if (includeUiContexts || !(service instanceof WindowProviderService)) { if (includeUiContexts || !(service instanceof WindowProviderService)) {
@@ -7524,12 +7520,6 @@ public final class ActivityThread extends ClientTransactionHandler
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
synchronized (mResourcesManager) { synchronized (mResourcesManager) {
// TODO (b/135719017): Temporary log for debugging IME service.
if (Build.IS_DEBUGGABLE && mHasImeComponent) {
Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "
+ "config=" + globalConfig);
}
// We need to apply this change to the resources immediately, because upon returning // We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it. // the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResources(globalConfig, if (mResourcesManager.applyConfigurationToResources(globalConfig,
@@ -7884,11 +7874,6 @@ public final class ActivityThread extends ClientTransactionHandler
return mDensityCompatMode; return mDensityCompatMode;
} }
@Override
public boolean hasImeComponent() {
return mHasImeComponent;
}
// ------------------ Regular JNI ------------------------ // ------------------ Regular JNI ------------------------
private native void nPurgePendingResources(); private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd); private native void nDumpGraphicsInfo(FileDescriptor fd);

View File

@@ -32,8 +32,6 @@ interface ActivityThreadInternal {
boolean isInDensityCompatMode(); boolean isInDensityCompatMode();
boolean hasImeComponent();
boolean isCachedProcessState(); boolean isCachedProcessState();
Application getApplication(); Application getApplication();

View File

@@ -28,12 +28,9 @@ import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.HardwareRenderer; import android.graphics.HardwareRenderer;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.os.LocaleList; import android.os.LocaleList;
import android.os.Trace; import android.os.Trace;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal;
@@ -168,12 +165,7 @@ class ConfigurationController {
mPendingConfiguration = null; mPendingConfiguration = null;
} }
final boolean hasIme = mActivityThread.hasImeComponent();
if (config == null) { if (config == null) {
// TODO (b/135719017): Temporary log for debugging IME service.
if (Build.IS_DEBUGGABLE && hasIme) {
Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
}
return; return;
} }
@@ -204,12 +196,6 @@ class ConfigurationController {
mConfiguration = new Configuration(); mConfiguration = new Configuration();
} }
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) { if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
// TODO (b/135719017): Temporary log for debugging IME service.
if (Build.IS_DEBUGGABLE && hasIme) {
Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
+ ", config=" + config
+ ", mConfiguration=" + mConfiguration);
}
return; return;
} }
@@ -237,13 +223,6 @@ class ConfigurationController {
ComponentCallbacks2 cb = callbacks.get(i); ComponentCallbacks2 cb = callbacks.get(i);
if (!equivalent) { if (!equivalent) {
performConfigurationChanged(cb, config); performConfigurationChanged(cb, config);
} else {
// TODO (b/135719017): Temporary log for debugging IME service.
if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
Log.w(TAG, "performConfigurationChanged didn't callback to IME "
+ ", configDiff=" + configDiff
+ ", mConfiguration=" + mConfiguration);
}
} }
} }
} }

View File

@@ -3194,12 +3194,6 @@ class ContextImpl extends Context {
@UnsupportedAppUsage @UnsupportedAppUsage
final void setOuterContext(@NonNull Context context) { final void setOuterContext(@NonNull Context context) {
mOuterContext = context; mOuterContext = context;
// TODO(b/149463653): check if we still need this method after migrating IMS to
// WindowContext.
if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) {
mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
mIsConfigurationBasedContext = true;
}
} }
@UnsupportedAppUsage @UnsupportedAppUsage

View File

@@ -18,16 +18,23 @@ package android.inputmethodservice;
import android.annotation.MainThread; import android.annotation.MainThread;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Service; import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSession;
import android.window.WindowProviderService;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -44,9 +51,22 @@ import java.io.PrintWriter;
* implement. This base class takes care of reporting your InputMethod from * implement. This base class takes care of reporting your InputMethod from
* the service when clients bind to it, but provides no standard implementation * the service when clients bind to it, but provides no standard implementation
* of the InputMethod interface itself. Derived classes must implement that * of the InputMethod interface itself. Derived classes must implement that
* interface. * interface.</p>
*
* <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
* input may not be the entire screen. For example, some devices may support to show the soft input
* on only half of screen.</p>
*
* <p>In that case, moving the soft input from one half screen to another will trigger a
* {@link android.content.res.Resources} update to match the new {@link Configuration} and
* this {@link AbstractInputMethodService} may also receive a
* {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
* </p>
*
* @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
* @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
*/ */
public abstract class AbstractInputMethodService extends Service public abstract class AbstractInputMethodService extends WindowProviderService
implements KeyEvent.Callback { implements KeyEvent.Callback {
private InputMethod mInputMethod; private InputMethod mInputMethod;
@@ -272,9 +292,33 @@ public abstract class AbstractInputMethodService extends Service
public void notifyUserActionIfNecessary() { public void notifyUserActionIfNecessary() {
} }
// TODO(b/149463653): remove it in T. We missed the API deadline in S.
/** @hide */ /** @hide */
@Override @Override
public final boolean isUiContext() { public final boolean isUiContext() {
return true; return true;
} }
/** @hide */
@Override
public final int getWindowType() {
return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
}
/** @hide */
@Override
@Nullable
public final Bundle getWindowContextOptions() {
return null;
}
/** @hide */
@Override
public final int getInitialDisplayId() {
try {
return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
} }

View File

@@ -629,10 +629,13 @@ public class InputMethodService extends AbstractInputMethodService {
throw new IllegalStateException( throw new IllegalStateException(
"attachToken() must be called at most once. token=" + token); "attachToken() must be called at most once. token=" + token);
} }
attachToWindowToken(token);
mToken = token; mToken = token;
mWindow.setToken(token); mWindow.setToken(token);
} }
// TODO(b/149463653): remove updateInputMethodDisplay(int displayId) since we'll get the
// right display by attachToWindowToken
/** /**
* {@inheritDoc} * {@inheritDoc}
* @hide * @hide

View File

@@ -865,4 +865,11 @@ interface IWindowManager
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener); void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
boolean isTaskSnapshotSupported(); boolean isTaskSnapshotSupported();
/**
* Returns the preferred display ID to show software keyboard.
*
* @see android.window.WindowProviderService#getLaunchedDisplayId
*/
int getImeDisplayId();
} }

View File

@@ -36,7 +36,6 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType; import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerImpl; import android.view.WindowManagerImpl;
// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
/** /**
* A {@link Service} responsible for showing a non-activity window, such as software keyboards or * A {@link Service} responsible for showing a non-activity window, such as software keyboards or
* accessibility overlay windows. This {@link Service} has similar behavior to * accessibility overlay windows. This {@link Service} has similar behavior to
@@ -89,6 +88,20 @@ public abstract class WindowProviderService extends Service {
return null; return null;
} }
/**
* Returns the display ID to launch this {@link WindowProviderService}.
*
* @hide
*/
@TestApi
@SuppressLint({"OnNameExpected"})
// Suppress the lint because it is not a callback and users may override this API to provide
// display.
@NonNull
public int getInitialDisplayId() {
return DEFAULT_DISPLAY;
}
/** /**
* Attaches this WindowProviderService to the {@code windowToken}. * Attaches this WindowProviderService to the {@code windowToken}.
* *
@@ -104,10 +117,9 @@ public abstract class WindowProviderService extends Service {
public final Context createServiceBaseContext(ActivityThread mainThread, public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) { LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo); final Context context = super.createServiceBaseContext(mainThread, packageInfo);
// Always associate with the default display at initialization. final Display display = context.getSystemService(DisplayManager.class)
final Display defaultDisplay = context.getSystemService(DisplayManager.class) .getDisplay(getInitialDisplayId());
.getDisplay(DEFAULT_DISPLAY); return context.createTokenContext(mWindowToken, display);
return context.createTokenContext(mWindowToken, defaultDisplay);
} }
@CallSuper @CallSuper

View File

@@ -26,8 +26,11 @@ import android.app.IWindowToken;
import android.app.ResourcesManager; import android.app.ResourcesManager;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -43,6 +46,8 @@ import java.lang.ref.WeakReference;
* @hide * @hide
*/ */
public class WindowTokenClient extends IWindowToken.Stub { public class WindowTokenClient extends IWindowToken.Stub {
private static final String TAG = WindowTokenClient.class.getSimpleName();
/** /**
* Attached {@link Context} for this window token to update configuration and resources. * Attached {@link Context} for this window token to update configuration and resources.
* Initialized by {@link #attachContext(Context)}. * Initialized by {@link #attachContext(Context)}.
@@ -53,6 +58,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
private final Configuration mConfiguration = new Configuration(); private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
/** /**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}. * can only attach one {@link Context}.
@@ -69,6 +76,8 @@ public class WindowTokenClient extends IWindowToken.Stub {
} }
mContextRef = new WeakReference<>(context); mContextRef = new WeakReference<>(context);
mConfiguration.setTo(context.getResources().getConfiguration()); mConfiguration.setTo(context.getResources().getConfiguration());
mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
&& context instanceof AbstractInputMethodService;
} }
@Override @Override
@@ -82,6 +91,13 @@ public class WindowTokenClient extends IWindowToken.Stub {
newConfig, newConfig /* overrideConfig */, displayChanged, newConfig, newConfig /* overrideConfig */, displayChanged,
null /* configChanged */); null /* configChanged */);
if (!shouldUpdateResources && mShouldDumpConfigForIme) {
Log.d(TAG, "Configuration not dispatch to IME because configuration is up"
+ " to date. Current config=" + context.getResources().getConfiguration()
+ ", reported config=" + mConfiguration
+ ", updated config=" + newConfig);
}
if (shouldUpdateResources) { if (shouldUpdateResources) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens // TODO(ag/9789103): update resource manager logic to track non-activity tokens
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
@@ -104,6 +120,13 @@ public class WindowTokenClient extends IWindowToken.Stub {
((WindowProviderService) context).onConfigurationChanged(newConfig)); ((WindowProviderService) context).onConfigurationChanged(newConfig));
} }
freeTextLayoutCachesIfNeeded(diff); freeTextLayoutCachesIfNeeded(diff);
if (diff == 0 && mShouldDumpConfigForIme) {
Log.d(TAG, "Configuration not dispatch to IME because configuration has no "
+ " public difference with updated config. "
+ " Current config=" + context.getResources().getConfiguration()
+ ", reported config=" + mConfiguration
+ ", updated config=" + newConfig);
}
mConfiguration.setTo(newConfig); mConfiguration.setTo(newConfig);
} }
if (displayChanged) { if (displayChanged) {

View File

@@ -32,7 +32,6 @@ import android.content.res.Configuration;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplay;
import android.inputmethodservice.InputMethodService;
import android.media.ImageReader; import android.media.ImageReader;
import android.os.UserHandle; import android.os.UserHandle;
import android.view.Display; import android.view.Display;
@@ -139,13 +138,6 @@ public class ContextTest {
assertThat(systemUiContext.isUiContext()).isTrue(); assertThat(systemUiContext.isUiContext()).isTrue();
} }
@Test
public void testIsUiContext_InputMethodService_returnsTrue() {
final InputMethodService ims = new InputMethodService();
assertTrue(ims.isUiContext());
}
@Test @Test
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() { public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */); verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);

View File

@@ -46,6 +46,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_P
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -8634,4 +8635,23 @@ public class WindowManagerService extends IWindowManager.Stub
return snapshot != null && snapshot.hasImeSurface(); return snapshot != null && snapshot.hasImeSurface();
} }
} }
@Override
public int getImeDisplayId() {
// TODO(b/189805422): Add a toast to notify users that IMS may get extra
// onConfigurationChanged callback when perDisplayFocus is enabled.
// Enabling perDisplayFocus means that we track focus on each display, so we don't have
// the "top focus" display and getTopFocusedDisplayContent returns the default display
// as the fallback. It leads to InputMethodService receives an extra onConfiguration
// callback when InputMethodService move from a secondary display to another display
// with the same display metrics because InputMethodService will always associate with
// the ImeContainer on the default display in onCreate and receive a configuration update
// to match default display ImeContainer and then receive another configuration update
// from attachToWindowToken.
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getTopFocusedDisplayContent();
return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId()
: DEFAULT_DISPLAY;
}
}
} }