blob: 484a5a8b53b8fa41f4424936ac72bac33bb8749a [file] [log] [blame]
/*
* 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.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.IWindow;
import android.view.InputApplicationHandle;
/**
* 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 {
private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
/* maps input token to an embedded window */
private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
private final Object mGlobalLock;
private final ActivityTaskManagerService mAtmService;
EmbeddedWindowController(ActivityTaskManagerService atmService) {
mAtmService = atmService;
mGlobalLock = atmService.getGlobalLock();
}
/**
* Adds a new embedded window.
*
* @param inputToken input channel token passed in by the embedding process when it requests
* the server to add an input channel to the embedded surface.
* @param window An {@link EmbeddedWindow} object to add to this controller.
*/
void add(IBinder inputToken, EmbeddedWindow window) {
try {
mWindows.put(inputToken, window);
updateProcessController(window);
window.mClient.asBinder().linkToDeath(()-> {
synchronized (mGlobalLock) {
mWindows.remove(inputToken);
}
}, 0);
} catch (RemoteException e) {
// The caller has died, remove from the map
mWindows.remove(inputToken);
}
}
/**
* Track the host activity in the embedding process so we can determine if the
* process is currently showing any UI to the user.
*/
private void updateProcessController(EmbeddedWindow window) {
if (window.mHostActivityRecord == null) {
return;
}
final WindowProcessController processController =
mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
if (processController == null) {
Slog.w(TAG, "Could not find the embedding process.");
} else {
processController.addHostActivity(window.mHostActivityRecord);
}
}
WindowState getHostWindow(IBinder inputToken) {
EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
}
void remove(IWindow client) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
mWindows.removeAt(i);
return;
}
}
}
void onWindowRemoved(WindowState host) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
if (mWindows.valueAt(i).mHostWindowState == host) {
mWindows.removeAt(i);
}
}
}
EmbeddedWindow get(IBinder inputToken) {
return mWindows.get(inputToken);
}
void onActivityRemoved(ActivityRecord activityRecord) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
final EmbeddedWindow window = mWindows.valueAt(i);
if (window.mHostActivityRecord == activityRecord) {
final WindowProcessController processController =
mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
if (processController != null) {
processController.removeHostActivity(activityRecord);
}
}
}
}
static class EmbeddedWindow {
final IWindow mClient;
@Nullable final WindowState mHostWindowState;
@Nullable final ActivityRecord mHostActivityRecord;
final int mOwnerUid;
final int mOwnerPid;
/**
* @param clientToken client token used to clean up the map if the embedding process dies
* @param hostWindowState input channel token belonging to the host window. This is needed
* to handle input callbacks to wm. It's used when raising ANR and
* when the user taps out side of the focused region on screen. This
* can be null if there is no host window.
* @param ownerUid calling uid
* @param ownerPid calling pid used for anr blaming
*/
EmbeddedWindow(IWindow clientToken, WindowState hostWindowState, int ownerUid,
int ownerPid) {
mClient = clientToken;
mHostWindowState = hostWindowState;
mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
: null;
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
String getName() {
final String hostWindowName = (mHostWindowState != null)
? mHostWindowState.getWindowTag().toString() : "Internal";
return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName
+ "}";
}
InputApplicationHandle getApplicationHandle() {
if (mHostWindowState == null
|| mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) {
return null;
}
return new InputApplicationHandle(
mHostWindowState.mInputWindowHandle.inputApplicationHandle);
}
}
}