From 03d65a72748a03e07c677911eeae325cd03cbf96 Mon Sep 17 00:00:00 2001 From: arthurhung Date: Tue, 28 Apr 2020 09:19:24 +0800 Subject: [PATCH] Fix consumer closed input channel cause an error occurred (1/2) An input channel specifies the file descriptors used to send input events to a window in another process. And All of the file descriptors open in the calling process shall be closed when process terminated. The server side could receive the socket broken event when the process terminated, we should do the unregister channel before close the file descriptor or do it automatically without error if there is no valid window. - Change the order that remove window before dispose the receiver. - Unregister input channel when windowless window removed. Bug: 133782251 Test: open app and exit by back key or recent apps, check if any error log occurs. Change-Id: I59d0084c2c771544e7ee226ce53c574f60c6b988 --- core/java/android/view/ViewRootImpl.java | 11 +++--- .../android/view/WindowlessWindowManager.java | 1 + .../server/wm/EmbeddedWindowController.java | 36 ++++++++++++++++--- .../server/wm/WindowManagerService.java | 21 ++--------- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 42f11c1624738..4b929d1f8988d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4636,14 +4636,17 @@ public final class ViewRootImpl implements ViewParent, mInputQueueCallback = null; mInputQueue = null; } - if (mInputEventReceiver != null) { - mInputEventReceiver.dispose(); - mInputEventReceiver = null; - } try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } + // Dispose receiver would dispose client InputChannel, too. That could send out a socket + // broken event, so we need to unregister the server InputChannel when removing window to + // prevent server side receive the event and prompt error. + if (mInputEventReceiver != null) { + mInputEventReceiver.dispose(); + mInputEventReceiver = null; + } mDisplayManager.unregisterDisplayListener(mDisplayListener); diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 15604a2ae2c1a..cd954c41f4693 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -191,6 +191,7 @@ public class WindowlessWindowManager implements IWindowSession { throw new IllegalArgumentException( "Invalid window token (never added or removed already)"); } + try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { t.remove(state.mSurfaceControl).apply(); } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 484a5a8b53b8f..9bbd4cd0778ac 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -28,6 +28,7 @@ import android.util.ArrayMap; import android.util.Slog; import android.view.IWindow; import android.view.InputApplicationHandle; +import android.view.InputChannel; /** * Keeps track of embedded windows. @@ -95,7 +96,7 @@ class EmbeddedWindowController { void remove(IWindow client) { for (int i = mWindows.size() - 1; i >= 0; i--) { if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) { - mWindows.removeAt(i); + mWindows.removeAt(i).onRemoved(); return; } } @@ -104,7 +105,7 @@ class EmbeddedWindowController { void onWindowRemoved(WindowState host) { for (int i = mWindows.size() - 1; i >= 0; i--) { if (mWindows.valueAt(i).mHostWindowState == host) { - mWindows.removeAt(i); + mWindows.removeAt(i).onRemoved(); } } } @@ -132,6 +133,8 @@ class EmbeddedWindowController { @Nullable final ActivityRecord mHostActivityRecord; final int mOwnerUid; final int mOwnerPid; + final WindowManagerService mWmService; + InputChannel mInputChannel; /** * @param clientToken client token used to clean up the map if the embedding process dies @@ -142,8 +145,9 @@ class EmbeddedWindowController { * @param ownerUid calling uid * @param ownerPid calling pid used for anr blaming */ - EmbeddedWindow(IWindow clientToken, WindowState hostWindowState, int ownerUid, - int ownerPid) { + EmbeddedWindow(WindowManagerService service, IWindow clientToken, + WindowState hostWindowState, int ownerUid, int ownerPid) { + mWmService = service; mClient = clientToken; mHostWindowState = hostWindowState; mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord @@ -167,5 +171,29 @@ class EmbeddedWindowController { return new InputApplicationHandle( mHostWindowState.mInputWindowHandle.inputApplicationHandle); } + + InputChannel openInputChannel() { + final String name = getName(); + + final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); + mInputChannel = inputChannels[0]; + final InputChannel clientChannel = inputChannels[1]; + mWmService.mInputManager.registerInputChannel(mInputChannel); + + if (mInputChannel.getToken() != clientChannel.getToken()) { + throw new IllegalStateException("Client and Server tokens are expected to" + + "be the same"); + } + + return clientChannel; + } + + void onRemoved() { + if (mInputChannel != null) { + mWmService.mInputManager.unregisterInputChannel(mInputChannel); + mInputChannel.dispose(); + mInputChannel = null; + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 73126c8b6b6a8..5b095b134827f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8015,26 +8015,15 @@ public class WindowManagerService extends IWindowManager.Stub IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) { final InputApplicationHandle applicationHandle; final String name; - final InputChannel[] inputChannels; final InputChannel clientChannel; - final InputChannel serverChannel; synchronized (mGlobalLock) { EmbeddedWindowController.EmbeddedWindow win = - new EmbeddedWindowController.EmbeddedWindow(window, + new EmbeddedWindowController.EmbeddedWindow(this, window, mInputToWindowMap.get(hostInputToken), callingUid, callingPid); - name = win.getName(); - - inputChannels = InputChannel.openInputChannelPair(name); - serverChannel = inputChannels[0]; - clientChannel = inputChannels[1]; - mInputManager.registerInputChannel(serverChannel); + clientChannel = win.openInputChannel(); mEmbeddedWindowController.add(clientChannel.getToken(), win); - if (serverChannel.getToken() != clientChannel.getToken()) { - throw new IllegalStateException("Client and Server channel are expected to" - + "be the same"); - } - applicationHandle = win.getApplicationHandle(); + name = win.getName(); } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, @@ -8042,10 +8031,6 @@ public class WindowManagerService extends IWindowManager.Stub clientChannel.transferTo(outInputChannel); clientChannel.dispose(); - // Prevent the java finalizer from breaking the input channel. But we won't - // do any further management so we just release the java ref and let the - // InputDispatcher hold the last ref. - serverChannel.release(); } private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,