From 94c9a8328e4dc9f8942ffc0d02784700ee77880b Mon Sep 17 00:00:00 2001 From: Tarandeep Singh Date: Mon, 3 Feb 2020 14:55:30 -0800 Subject: [PATCH] Remove IME surface when hidden When IME is no longer shown, notify IMS to hide the window surface. This reclaims the memory allocated to IME window surface. Fix: 150163310 Test: atest CtsInputMethodTests Manually: 1. Open any app that has editor and run adb shell dumpsys SurfaceFlinger | grep Total 2. Note down total memory 3. Tap on editor and IME shows. Note down memory using command in #1. It should go up by ~27MB 4. Hide IME and note down memory again. It should revert to original value in #1. Change-Id: I6c2a13dd40b22d3fd62b4763ba369992e5ff1138 --- .../IInputMethodSessionWrapper.java | 10 ++++++++++ .../inputmethodservice/InputMethodService.java | 15 +++++++++++++++ ...ltiClientInputMethodClientCallbackAdaptor.java | 6 ++++++ .../android/view/ImeInsetsSourceConsumer.java | 15 ++++++++++++++- core/java/android/view/InsetsController.java | 8 ++++---- core/java/android/view/InsetsSourceConsumer.java | 13 +++++++++++-- .../view/inputmethod/InputMethodManager.java | 15 +++++++++++++++ .../view/inputmethod/InputMethodSession.java | 6 ++++++ .../internal/view/IInputMethodSession.aidl | 2 ++ 9 files changed, 83 insertions(+), 7 deletions(-) diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 9327b241c6c51..e9de27456f975 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -53,6 +53,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_FINISH_SESSION = 110; private static final int DO_VIEW_CLICKED = 115; private static final int DO_NOTIFY_IME_HIDDEN = 120; + private static final int DO_REMOVE_IME_SURFACE = 130; @UnsupportedAppUsage HandlerCaller mCaller; @@ -136,6 +137,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.notifyImeHidden(); return; } + case DO_REMOVE_IME_SURFACE: { + mInputMethodSession.removeImeSurface(); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -183,6 +188,11 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_IME_HIDDEN)); } + @Override + public void removeImeSurface() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_IME_SURFACE)); + } + @Override public void updateCursor(Rect newCursor) { mCaller.executeOrSendMessage( diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 20a4ab35defec..61adfbce30bff 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -814,6 +814,13 @@ public class InputMethodService extends AbstractInputMethodService { onPreRenderedWindowVisibilityChanged(false /* setVisible */); } + private void removeImeSurface() { + if (!mShowInputRequested && !mWindowVisible) { + // hiding a window removes its surface. + mWindow.hide(); + } + } + private void setImeWindowStatus(int visibilityFlags, int backDisposition) { mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); } @@ -932,6 +939,14 @@ public class InputMethodService extends AbstractInputMethodService { public final void notifyImeHidden() { InputMethodService.this.notifyImeHidden(); } + + /** + * Notify IME that surface can be now removed. + * @hide + */ + public final void removeImeSurface() { + InputMethodService.this.removeImeSurface(); + } } /** diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index 31c948a14698f..ef138a0c2217e 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -296,6 +296,12 @@ final class MultiClientInputMethodClientCallbackAdaptor { // no-op for multi-session since IME is responsible controlling navigation bar buttons. reportNotSupported(); } + + @Override + public void removeImeSurface() { + // no-op for multi-session + reportNotSupported(); + } } private static final class MultiClientInputMethodSessionImpl diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index f2263693897b8..43afc15072cee 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -17,7 +17,6 @@ package android.view; import static android.view.InsetsState.ITYPE_IME; -import static android.view.InsetsState.toPublicType; import android.annotation.Nullable; import android.inputmethodservice.InputMethodService; @@ -99,6 +98,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } } + @Override + void hide(boolean animationFinished) { + super.hide(); + if (animationFinished) { + // remove IME surface as IME has finished hide animation. + removeSurface(); + } + } + /** * Request {@link InputMethodManager} to show the IME. * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}. @@ -127,6 +135,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { getImm().notifyImeHidden(); } + @Override + public void removeSurface() { + getImm().removeImeSurface(); + } + @Override public void setControl(@Nullable InsetsSourceControl control, int[] showTypes, int[] hideTypes) { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 4a6a5a0312af3..65ea6bb528f89 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -694,7 +694,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (shown) { showDirectly(controller.getTypes()); } else { - hideDirectly(controller.getTypes()); + hideDirectly(controller.getTypes(), true /* animationFinished */); } } @@ -852,10 +852,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); } - private void hideDirectly(@InsetsType int types) { + private void hideDirectly(@InsetsType int types, boolean animationFinished) { final ArraySet internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { - getSourceConsumer(internalTypes.valueAt(i)).hide(); + getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished); } } @@ -887,7 +887,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { showDirectly(types); } else { - hideDirectly(types); + hideDirectly(types, false /* animationFinished */); } if (mViewRoot.mView == null) { return; diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index e6497c00c8ddd..e3a7de126df31 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -16,12 +16,10 @@ package android.view; -import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsState.toPublicType; import android.annotation.IntDef; import android.annotation.Nullable; -import android.util.MutableShort; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type.InsetsType; @@ -137,6 +135,10 @@ public class InsetsSourceConsumer { setRequestedVisible(false); } + void hide(boolean animationFinished) { + hide(); + } + /** * Called when current window gains focus */ @@ -200,6 +202,13 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. } + /** + * Remove surface on which this consumer type is drawn. + */ + public void removeSurface() { + // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. + } + /** * Sets requested visibility from the client, regardless of whether we are able to control it at * the moment. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 39d5f5c396a13..16a45322c9663 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2055,6 +2055,21 @@ public final class InputMethodManager { } } + /** + * Notify IME directly to remove surface as it is no longer visible. + * @hide + */ + public void removeImeSurface() { + synchronized (mH) { + try { + if (mCurMethod != null) { + mCurMethod.removeImeSurface(); + } + } catch (RemoteException re) { + } + } + } + /** * Report the current selection range. * diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index eb81628f9e275..0d688ffa44c8a 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -191,4 +191,10 @@ public interface InputMethodSession { * @hide */ public void notifyImeHidden(); + + /** + * Notify IME directly to remove surface as it is no longer visible. + * @hide + */ + public void removeImeSurface(); } diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 664643cc9b4d7..0319e36373849 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -50,4 +50,6 @@ oneway interface IInputMethodSession { void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo); void notifyImeHidden(); + + void removeImeSurface(); }