1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 22 23 import android.annotation.Nullable; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.os.UserHandle; 27 import android.util.ArrayMap; 28 import android.util.Slog; 29 import android.view.IWindow; 30 import android.view.InputApplicationHandle; 31 import android.view.InputChannel; 32 33 /** 34 * Keeps track of embedded windows. 35 * 36 * If the embedded window does not receive input then Window Manager does not keep track of it. 37 * But if they do receive input, we keep track of the calling PID to blame the right app and 38 * the host window to send pointerDownOutsideFocus. 39 */ 40 class EmbeddedWindowController { 41 private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM; 42 /* maps input token to an embedded window */ 43 private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>(); 44 private final Object mGlobalLock; 45 private final ActivityTaskManagerService mAtmService; 46 EmbeddedWindowController(ActivityTaskManagerService atmService)47 EmbeddedWindowController(ActivityTaskManagerService atmService) { 48 mAtmService = atmService; 49 mGlobalLock = atmService.getGlobalLock(); 50 } 51 52 /** 53 * Adds a new embedded window. 54 * 55 * @param inputToken input channel token passed in by the embedding process when it requests 56 * the server to add an input channel to the embedded surface. 57 * @param window An {@link EmbeddedWindow} object to add to this controller. 58 */ add(IBinder inputToken, EmbeddedWindow window)59 void add(IBinder inputToken, EmbeddedWindow window) { 60 try { 61 mWindows.put(inputToken, window); 62 updateProcessController(window); 63 window.mClient.asBinder().linkToDeath(()-> { 64 synchronized (mGlobalLock) { 65 mWindows.remove(inputToken); 66 } 67 }, 0); 68 } catch (RemoteException e) { 69 // The caller has died, remove from the map 70 mWindows.remove(inputToken); 71 } 72 } 73 74 /** 75 * Track the host activity in the embedding process so we can determine if the 76 * process is currently showing any UI to the user. 77 */ updateProcessController(EmbeddedWindow window)78 private void updateProcessController(EmbeddedWindow window) { 79 if (window.mHostActivityRecord == null) { 80 return; 81 } 82 final WindowProcessController processController = 83 mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid); 84 if (processController == null) { 85 Slog.w(TAG, "Could not find the embedding process."); 86 } else { 87 processController.addHostActivity(window.mHostActivityRecord); 88 } 89 } 90 getHostWindow(IBinder inputToken)91 WindowState getHostWindow(IBinder inputToken) { 92 EmbeddedWindow embeddedWindow = mWindows.get(inputToken); 93 return embeddedWindow != null ? embeddedWindow.mHostWindowState : null; 94 } 95 remove(IWindow client)96 void remove(IWindow client) { 97 for (int i = mWindows.size() - 1; i >= 0; i--) { 98 if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) { 99 mWindows.removeAt(i).onRemoved(); 100 return; 101 } 102 } 103 } 104 onWindowRemoved(WindowState host)105 void onWindowRemoved(WindowState host) { 106 for (int i = mWindows.size() - 1; i >= 0; i--) { 107 if (mWindows.valueAt(i).mHostWindowState == host) { 108 mWindows.removeAt(i).onRemoved(); 109 } 110 } 111 } 112 get(IBinder inputToken)113 EmbeddedWindow get(IBinder inputToken) { 114 return mWindows.get(inputToken); 115 } 116 onActivityRemoved(ActivityRecord activityRecord)117 void onActivityRemoved(ActivityRecord activityRecord) { 118 for (int i = mWindows.size() - 1; i >= 0; i--) { 119 final EmbeddedWindow window = mWindows.valueAt(i); 120 if (window.mHostActivityRecord == activityRecord) { 121 final WindowProcessController processController = 122 mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid); 123 if (processController != null) { 124 processController.removeHostActivity(activityRecord); 125 } 126 } 127 } 128 } 129 130 static class EmbeddedWindow { 131 final IWindow mClient; 132 @Nullable final WindowState mHostWindowState; 133 @Nullable final ActivityRecord mHostActivityRecord; 134 final int mOwnerUid; 135 final int mOwnerPid; 136 final WindowManagerService mWmService; 137 InputChannel mInputChannel; 138 final int mWindowType; 139 140 /** 141 * @param clientToken client token used to clean up the map if the embedding process dies 142 * @param hostWindowState input channel token belonging to the host window. This is needed 143 * to handle input callbacks to wm. It's used when raising ANR and 144 * when the user taps out side of the focused region on screen. This 145 * can be null if there is no host window. 146 * @param ownerUid calling uid 147 * @param ownerPid calling pid used for anr blaming 148 */ EmbeddedWindow(WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType)149 EmbeddedWindow(WindowManagerService service, IWindow clientToken, 150 WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) { 151 mWmService = service; 152 mClient = clientToken; 153 mHostWindowState = hostWindowState; 154 mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord 155 : null; 156 mOwnerUid = ownerUid; 157 mOwnerPid = ownerPid; 158 mWindowType = windowType; 159 } 160 getName()161 String getName() { 162 final String hostWindowName = (mHostWindowState != null) 163 ? mHostWindowState.getWindowTag().toString() : "Internal"; 164 return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName 165 + "}"; 166 } 167 getApplicationHandle()168 InputApplicationHandle getApplicationHandle() { 169 if (mHostWindowState == null 170 || mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) { 171 return null; 172 } 173 return new InputApplicationHandle( 174 mHostWindowState.mInputWindowHandle.inputApplicationHandle); 175 } 176 openInputChannel()177 InputChannel openInputChannel() { 178 final String name = getName(); 179 180 final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); 181 mInputChannel = inputChannels[0]; 182 final InputChannel clientChannel = inputChannels[1]; 183 mWmService.mInputManager.registerInputChannel(mInputChannel); 184 185 if (mInputChannel.getToken() != clientChannel.getToken()) { 186 throw new IllegalStateException("Client and Server tokens are expected to" 187 + "be the same"); 188 } 189 190 return clientChannel; 191 } 192 onRemoved()193 void onRemoved() { 194 if (mInputChannel != null) { 195 mWmService.mInputManager.unregisterInputChannel(mInputChannel); 196 mInputChannel.dispose(); 197 mInputChannel = null; 198 } 199 } 200 } 201 } 202