Update input policy to handle embedded windows

ANR - If embedded windows are slow in handling inputs the system should blame the embedded app.

PointerDownOutsideFocus - if a user taps outside the currently focused window onto an
embedded window, treat it as if the host window was tapped.

Rename blessInputSurface -> grantInputChannel and add a name to embedded windows.

Bug: 134365580
Test: b WindowlessWmTest
Test: atest CtsWindowManagerDeviceTestCases:WindowlessWmTests

Change-Id: If88970cf6ce17669b41fec995535151a492fab12
diff --git a/api/test-current.txt b/api/test-current.txt
index 466320b..5e2d4a5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4415,6 +4415,10 @@
     method public abstract String asyncImpl() default "";
   }
 
+  public class SurfaceView extends android.view.View {
+    method @Nullable public android.os.IBinder getInputToken();
+  }
+
   @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
     method public android.view.View getTooltipView();
     method public boolean isAutofilled();
@@ -4462,8 +4466,9 @@
   }
 
   public class WindowlessViewRoot {
-    ctor public WindowlessViewRoot(android.content.Context, android.view.Display, android.view.SurfaceControl);
+    ctor public WindowlessViewRoot(@NonNull android.content.Context, @NonNull android.view.Display, @NonNull android.view.SurfaceControl, @Nullable android.os.IBinder);
     method public void addView(android.view.View, android.view.WindowManager.LayoutParams);
+    method public void dispose();
     method public void relayout(android.view.WindowManager.LayoutParams);
   }
 
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 6ce5ac4..eaf6fca 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -314,5 +314,6 @@
     * Request the server to call setInputWindowInfo on a given Surface, and return
     * an input channel where the client can receive input.
     */
-    void blessInputSurface(int displayId, in SurfaceControl surface, out InputChannel outInputChannel);
+    void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
+            in IBinder hostInputToken, out InputChannel outInputChannel);
 }
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index 5f6bc23..3d05e2a 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -36,7 +36,7 @@
     // Dispatching timeout.
     public long dispatchingTimeoutNanos;
 
-    public IBinder token;
+    public final IBinder token;
 
     private native void nativeDispose();
 
@@ -44,6 +44,12 @@
         this.token = token;
     }
 
+    public InputApplicationHandle(InputApplicationHandle handle) {
+        this.token = handle.token;
+        this.dispatchingTimeoutNanos = handle.dispatchingTimeoutNanos;
+        this.name = handle.name;
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index ed8492e..3080b42 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.util.Log;
@@ -184,6 +185,16 @@
         return false;
     }
 
+    /**
+     * @return Returns a token to identify the input channel.
+     */
+    public IBinder getToken() {
+        if (mInputChannel == null) {
+            return null;
+        }
+        return mInputChannel.getToken();
+    }
+
     // Called from native code.
     @SuppressWarnings("unused")
     @UnsupportedAppUsage
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 59e9ed1..0b5af2d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -20,6 +20,8 @@
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
 
+import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
@@ -34,6 +36,7 @@
 import android.graphics.RenderNode;
 import android.os.Build;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -1444,6 +1447,19 @@
     }
 
     /**
+     * @return The token used to identify the windows input channel.
+     * @hide
+     */
+    @TestApi
+    public @Nullable IBinder getInputToken() {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return null;
+        }
+        return viewRoot.getInputToken();
+    }
+
+    /**
      * Set window stopped to false and update surface visibility when ViewRootImpl surface is
      * created.
      * @hide
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 85bf19f..97adaf5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -69,6 +69,7 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -1661,7 +1662,7 @@
 
         return mBlastBufferQueue.getSurface();
     }
-    
+
     private void setBoundsLayerCrop() {
         // mWinFrame is already adjusted for surface insets. So offset it and use it as
         // the cropping bounds.
@@ -7179,7 +7180,7 @@
         if (mSurfaceControl.isValid()) {
             if (USE_BLAST_BUFFERQUEUE == false) {
                 mSurface.copyFrom(mSurfaceControl);
-            } else { 
+            } else {
                 mSurface.transferFrom(getOrCreateBLASTSurface(
                     (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                     (int) (mView.getMeasuredHeight() * appScale + 0.5f)));
@@ -8932,6 +8933,16 @@
     }
 
     /**
+     * @return Returns a token used to identify the windows input channel.
+     */
+    public IBinder getInputToken() {
+        if (mInputEventReceiver == null) {
+            return null;
+        }
+        return mInputEventReceiver.getToken();
+    }
+
+    /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
      */
diff --git a/core/java/android/view/WindowlessViewRoot.java b/core/java/android/view/WindowlessViewRoot.java
index c2500b8..addf8e2 100644
--- a/core/java/android/view/WindowlessViewRoot.java
+++ b/core/java/android/view/WindowlessViewRoot.java
@@ -16,12 +16,11 @@
 
 package android.view;
 
-import android.content.res.Resources;
-import android.content.Context;
-import android.view.SurfaceControl;
-import android.view.View;
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.content.Context;
+import android.os.IBinder;
 
 /**
  * Utility class for adding a view hierarchy to a SurfaceControl.
@@ -31,10 +30,13 @@
  */
 @TestApi
 public class WindowlessViewRoot {
-    ViewRootImpl mViewRoot;
-    WindowlessWindowManager mWm;
-    public WindowlessViewRoot(Context c, Display d, SurfaceControl rootSurface) {
-        mWm = new WindowlessWindowManager(c.getResources().getConfiguration(), rootSurface);
+    private ViewRootImpl mViewRoot;
+    private WindowlessWindowManager mWm;
+    public WindowlessViewRoot(@NonNull Context c, @NonNull Display d,
+            @NonNull SurfaceControl rootSurface,
+            @Nullable IBinder hostInputToken) {
+        mWm = new WindowlessWindowManager(c.getResources().getConfiguration(), rootSurface,
+                hostInputToken);
         mViewRoot = new ViewRootImpl(c, d, mWm);
     }
 
@@ -49,4 +51,8 @@
             t.apply();
         });
     }
+
+    public void dispose() {
+        mViewRoot.dispatchDetachedFromWindow();
+    }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 6c6046f..403bfda 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,8 +24,6 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.IWindowSession;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 import java.util.HashMap;
 
@@ -60,18 +58,20 @@
     final HashMap<IBinder, ResizeCompleteCallback> mResizeCompletionForWindow =
         new HashMap<IBinder, ResizeCompleteCallback>();
 
-    final SurfaceSession mSurfaceSession = new SurfaceSession();
-    final SurfaceControl mRootSurface;
-    final Configuration mConfiguration;
-    IWindowSession mRealWm;
+    private final SurfaceSession mSurfaceSession = new SurfaceSession();
+    private final SurfaceControl mRootSurface;
+    private final Configuration mConfiguration;
+    private final IWindowSession mRealWm;
+    private final IBinder mHostInputToken;
 
     private int mForceHeight = -1;
     private int mForceWidth = -1;
 
-    WindowlessWindowManager(Configuration c, SurfaceControl rootSurface) {
+    WindowlessWindowManager(Configuration c, SurfaceControl rootSurface, IBinder hostInputToken) {
         mRootSurface = rootSurface;
         mConfiguration = new Configuration(c);
         mRealWm = WindowManagerGlobal.getWindowSession();
+        mHostInputToken = hostInputToken;
     }
 
     /**
@@ -87,6 +87,7 @@
     /**
      * IWindowSession implementation.
      */
+    @Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
@@ -101,10 +102,11 @@
             mStateForWindow.put(window.asBinder(), new State(sc, attrs));
         }
 
-        if ((attrs.inputFeatures &
-                WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
+        if (((attrs.inputFeatures &
+                WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) &&
+                (mHostInputToken != null)) {
             try {
-                mRealWm.blessInputSurface(displayId, sc, outInputChannel);
+                mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, outInputChannel);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to bless surface: " + e);
             }
@@ -122,10 +124,12 @@
     }
 
     @Override
-    public void remove(android.view.IWindow window) {}
+    public void remove(android.view.IWindow window) throws RemoteException {
+        mRealWm.remove(window);
+    }
 
     private boolean isOpaque(WindowManager.LayoutParams attrs) {
-        if (attrs.surfaceInsets != null && attrs.surfaceInsets.left != 0 || 
+        if (attrs.surfaceInsets != null && attrs.surfaceInsets.left != 0 ||
                 attrs.surfaceInsets.top != 0 || attrs.surfaceInsets.right != 0 ||
                 attrs.surfaceInsets.bottom != 0) {
             return false;
@@ -326,8 +330,8 @@
     }
 
     @Override
-    public void blessInputSurface(int displayId, SurfaceControl surface,
-            InputChannel outInputChannel) {
+    public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
+            IBinder hostInputToken, InputChannel outInputChannel) {
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
new file mode 100644
index 0000000..24948a2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 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 com.android.server.wm;
+
+import static com.android.server.wm.ActivityRecord.INVALID_PID;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.view.IWindow;
+
+/**
+ * Keeps track of embedded windows.
+ *
+ * If the embedded window does not receive input then Window Manager does not keep track of it.
+ * But if they do receive input, we keep track of the calling PID to blame the right app and
+ * the host window to send pointerDownOutsideFocus.
+ */
+class EmbeddedWindowController {
+    /* maps input token to an embedded window */
+    private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
+    private final Object mWmLock;
+
+    EmbeddedWindowController(Object wmLock) {
+        mWmLock = wmLock;
+    }
+
+    void add(IBinder inputToken, IWindow window, WindowState hostWindowState, int ownerUid,
+            int ownerPid) {
+        EmbeddedWindow embeddedWindow = new EmbeddedWindow(window, hostWindowState, ownerUid,
+                ownerPid);
+        try {
+            mWindows.put(inputToken, embeddedWindow);
+            window.asBinder().linkToDeath(()-> {
+                synchronized (mWmLock) {
+                    mWindows.remove(inputToken);
+                }
+            }, 0);
+        } catch (RemoteException e) {
+            // The caller has died, remove from the map
+            mWindows.remove(inputToken);
+        }
+    }
+
+    WindowState getHostWindow(IBinder inputToken) {
+        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+        return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
+    }
+
+    int getOwnerPid(IBinder inputToken) {
+        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+        return embeddedWindow != null ? embeddedWindow.mOwnerPid : INVALID_PID;
+    }
+
+    void remove(IWindow client) {
+        for (ArrayMap.Entry<IBinder, EmbeddedWindow> entry: mWindows.entrySet()) {
+            if (entry.getValue().mClient.asBinder() == client.asBinder()) {
+                mWindows.remove(entry.getKey());
+                return;
+            }
+        }
+    }
+
+    void removeWindowsWithHost(WindowState host) {
+        for (ArrayMap.Entry<IBinder, EmbeddedWindow> entry: mWindows.entrySet()) {
+            if (entry.getValue().mHostWindowState == host) {
+                mWindows.remove(entry.getKey());
+            }
+        }
+    }
+
+    private static class EmbeddedWindow {
+        final IWindow mClient;
+        final WindowState mHostWindowState;
+        final int mOwnerUid;
+        final int mOwnerPid;
+
+        EmbeddedWindow(IWindow clientToken, WindowState hostWindowState, int ownerUid,
+                int ownerPid) {
+            mClient = clientToken;
+            mHostWindowState = hostWindowState;
+            mOwnerUid = ownerUid;
+            mOwnerPid = ownerPid;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1ebbb02..2a6fb4a 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -87,12 +87,20 @@
         ActivityRecord activity = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
+        int windowPid = INVALID_PID;
         //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
         synchronized (mService.mGlobalLock) {
             if (token != null) {
                 windowState = mService.mInputToWindowMap.get(token);
                 if (windowState != null) {
                     activity = windowState.mActivityRecord;
+                    windowPid = windowState.mSession.mPid;
+                } else {
+                    // Check if this is an embedded window and if so get the embedded app pid
+                    windowPid = mService.mEmbeddedWindowController.getOwnerPid(token);
+                    WindowState hostWindowState =
+                            mService.mEmbeddedWindowController.getHostWindow(token);
+                    aboveSystem = isWindowAboveSystem(hostWindowState);
                 }
             }
 
@@ -107,9 +115,7 @@
                 // Figure out whether this window is layered above system windows.
                 // We need to do this here to help the activity manager know how to
                 // layer its ANR dialog.
-                int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
-                        TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
-                aboveSystem = windowState.mBaseLayer > systemAlertLayer;
+                aboveSystem = isWindowAboveSystem(windowState);
             } else if (activity != null) {
                 Slog.i(TAG_WM, "Input event dispatching timed out "
                         + "sending to application " + activity.stringName
@@ -128,18 +134,17 @@
         if (activity != null && activity.appToken != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            final boolean abort = activity.keyDispatchingTimedOut(reason,
-                    (windowState != null) ? windowState.mSession.mPid : INVALID_PID);
+            final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid);
             if (!abort) {
                 // The activity manager declined to abort dispatching.
                 // Wait a bit longer and timeout again later.
                 return activity.mInputDispatchingTimeoutNanos;
             }
-        } else if (windowState != null) {
+        } else if (windowState != null || windowPid != INVALID_PID) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            long timeout = mService.mAmInternal.inputDispatchingTimedOut(
-                    windowState.mSession.mPid, aboveSystem, reason);
+            long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem,
+                    reason);
             if (timeout >= 0) {
                 // The activity manager declined to abort dispatching.
                 // Wait a bit longer and timeout again later.
@@ -149,6 +154,12 @@
         return 0; // abort dispatching
     }
 
+    private boolean isWindowAboveSystem(WindowState windowState) {
+        int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
+                TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
+        return windowState.mBaseLayer > systemAlertLayer;
+    }
+
     /** Notifies that the input device configuration has changed. */
     @Override
     public void notifyConfigurationChanged() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 06e7d66..96be7cc 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -623,13 +623,14 @@
         return false;
     }
 
-    public void blessInputSurface(int displayId, SurfaceControl surface,
-            InputChannel outInputChannel) {
+    public void grantInputChannel(int displayId, SurfaceControl surface,
+            IWindow window, IBinder hostInputToken, InputChannel outInputChannel) {
         final int callerUid = Binder.getCallingUid();
         final int callerPid = Binder.getCallingPid();
         final long identity = Binder.clearCallingIdentity();
         try {
-            mService.blessInputSurface(callerUid, callerPid, displayId, surface, outInputChannel);
+            mService.grantInputChannel(callerUid, callerPid, displayId, surface, window,
+                    hostInputToken, outInputChannel);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2c56b11..56d36e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -224,6 +224,7 @@
 import android.view.IWindowManager;
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
+import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -698,6 +699,7 @@
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     SettingsObserver mSettingsObserver;
+    final EmbeddedWindowController mEmbeddedWindowController;
 
     @VisibleForTesting
     final class SettingsObserver extends ContentObserver {
@@ -1283,6 +1285,7 @@
                 new HandlerExecutor(mH), mPropertiesChangedListener);
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
+        mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock);
     }
 
     /**
@@ -1868,10 +1871,13 @@
     void removeWindow(Session session, IWindow client) {
         synchronized (mGlobalLock) {
             WindowState win = windowForClientLocked(session, client, false);
-            if (win == null) {
+            if (win != null) {
+                win.removeIfPossible();
                 return;
             }
-            win.removeIfPossible();
+
+            // Remove embedded window map if the token belongs to an embedded window
+            mEmbeddedWindowController.remove(client);
         }
     }
 
@@ -1894,6 +1900,7 @@
         if (dc.mCurrentFocus == null) {
             dc.mWinRemovedSinceNullFocus.add(win);
         }
+        mEmbeddedWindowController.removeWindowsWithHost(win);
         mPendingRemove.remove(win);
         mResizingWindows.remove(win);
         updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
@@ -4574,7 +4581,7 @@
                     }
 
                     // First notify the accessibility manager for the change so it has
-                    // the windows before the newly focused one starts firing eventgs.
+                    // the windows before the newly focused one starts firing events.
                     if (accessibilityController != null) {
                         accessibilityController.onWindowFocusChangedNotLocked(
                                 displayContent.getDisplayId());
@@ -7643,7 +7650,12 @@
     }
 
     private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
-        final WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
+        WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
+        if (touchedWindow == null) {
+            // if a user taps outside the currently focused window onto an embedded window, treat
+            // it as if the host window was tapped.
+            touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
+        }
         if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
             return;
         }
@@ -7706,20 +7718,37 @@
      * Used by WindowlessWindowManager to enable input on SurfaceControl embedded
      * views.
      */
-    void blessInputSurface(int callingUid, int callingPid, int displayId, SurfaceControl surface,
-            InputChannel outInputChannel) {
-        String name = "Blessed Surface";
-        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
-        InputChannel inputChannel = inputChannels[0];
-        InputChannel clientChannel = inputChannels[1];
+    void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface,
+            IWindow window, IBinder hostInputToken, InputChannel outInputChannel) {
+        final InputApplicationHandle applicationHandle;
+        final String name;
+        final InputChannel[] inputChannels;
+        final InputChannel clientChannel;
+        final InputChannel serverChannel;
+        synchronized (mGlobalLock) {
+            final WindowState hostWindow = mInputToWindowMap.get(hostInputToken);
+            if (hostWindow == null) {
+                Slog.e(TAG, "Failed to grant input channel");
+                return;
+            }
+            name = "EmbeddedWindow{ u" + UserHandle.getUserId(callingUid)
+                    + " " + hostWindow.getWindowTag() + "}";
+
+            inputChannels = InputChannel.openInputChannelPair(name);
+            serverChannel = inputChannels[0];
+            clientChannel = inputChannels[1];
+            mInputManager.registerInputChannel(serverChannel);
+            mEmbeddedWindowController.add(serverChannel.getToken(), window, hostWindow, callingUid,
+                    callingPid);
+            applicationHandle = new InputApplicationHandle(
+                hostWindow.mInputWindowHandle.inputApplicationHandle);
+        }
 
         clientChannel.transferTo(outInputChannel);
         clientChannel.dispose();
 
-        mInputManager.registerInputChannel(inputChannel);
-
-        InputWindowHandle h = new InputWindowHandle(null, displayId);
-        h.token = inputChannel.getToken();
+        InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
+        h.token = serverChannel.getToken();
         h.name = name;
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
         h.layoutParamsType = 0;
@@ -7744,7 +7773,7 @@
         // 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.
-        inputChannel.release();
+        serverChannel.release();
     }
 
     /** Return whether layer tracing is enabled */
diff --git a/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java b/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java
index 5a146da..5c1e830 100644
--- a/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java
+++ b/tests/WindowlessWmTest/src/com/android/test/viewembed/WindowlessWmTest.java
@@ -17,17 +17,16 @@
 package com.android.test.viewembed;
 
 import android.app.Activity;
-import android.os.Bundle;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
+import android.os.Bundle;
 import android.view.Gravity;
-import android.view.WindowlessViewRoot;
 import android.view.SurfaceHolder;
-import android.view.SurfaceHolder.Callback;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowlessViewRoot;
 import android.widget.Button;
 import android.widget.FrameLayout;
 
@@ -50,8 +49,8 @@
 
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
-        mVr = new WindowlessViewRoot(this, this.getDisplay(),
-                mView.getSurfaceControl());
+        mVr = new WindowlessViewRoot(this, this.getDisplay(), mView.getSurfaceControl(),
+                mView.getInputToken());
         Button v = new Button(this);
         v.setBackgroundColor(Color.BLUE);
         v.setOnClickListener(new View.OnClickListener() {