Merge "Fix WindowContext leak" into rvc-dev

This commit is contained in:
Charles Chen
2020-04-09 07:39:29 +00:00
committed by Android (Google) Code Review
13 changed files with 309 additions and 65 deletions

View File

@@ -30,4 +30,6 @@ import android.view.IWindow;
*/ */
oneway interface IWindowToken { oneway interface IWindowToken {
void onConfigurationChanged(in Configuration newConfig, int newDisplayId); void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
void onWindowTokenRemoved();
} }

View File

@@ -28,6 +28,8 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl; import android.view.WindowManagerImpl;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.Reference; import java.lang.ref.Reference;
/** /**
@@ -75,8 +77,6 @@ public class WindowContext extends ContextWrapper {
// config back to the client. // config back to the client.
result = mWms.addWindowTokenWithOptions( result = mWms.addWindowTokenWithOptions(
mToken, type, getDisplayId(), options, getPackageName()); mToken, type, getDisplayId(), options, getPackageName());
// TODO(window-context): remove token with a DeathObserver
} catch (RemoteException e) { } catch (RemoteException e) {
mOwnsToken = false; mOwnsToken = false;
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
@@ -100,6 +100,13 @@ public class WindowContext extends ContextWrapper {
@Override @Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
release();
super.finalize();
}
/** Used for test to invoke because we can't invoke finalize directly. */
@VisibleForTesting
public void release() {
if (mOwnsToken) { if (mOwnsToken) {
try { try {
mWms.removeWindowToken(mToken, getDisplayId()); mWms.removeWindowToken(mToken, getDisplayId());
@@ -108,6 +115,12 @@ public class WindowContext extends ContextWrapper {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
super.finalize(); destroy();
}
void destroy() {
final ContextImpl impl = (ContextImpl) getBaseContext();
impl.scheduleFinalCleanup(getClass().getName(), "WindowContext");
Reference.reachabilityFence(this);
} }
} }

View File

@@ -20,6 +20,9 @@ import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.view.WindowManagerGlobal;
import java.lang.ref.WeakReference;
/** /**
* Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from * Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from
@@ -31,9 +34,9 @@ import android.os.IBinder;
public class WindowTokenClient extends IWindowToken.Stub { public class WindowTokenClient extends IWindowToken.Stub {
/** /**
* Attached {@link Context} for this window token to update configuration and resources. * Attached {@link Context} for this window token to update configuration and resources.
* Initialized by {@link #attachContext(Context)}. * Initialized by {@link #attachContext(WindowContext)}.
*/ */
private Context mContext = null; private WeakReference<WindowContext> mContextRef = null;
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
@@ -47,30 +50,46 @@ public class WindowTokenClient extends IWindowToken.Stub {
* @param context context to be attached * @param context context to be attached
* @throws IllegalStateException if attached context has already existed. * @throws IllegalStateException if attached context has already existed.
*/ */
void attachContext(@NonNull Context context) { void attachContext(@NonNull WindowContext context) {
if (mContext != null) { if (mContextRef != null) {
throw new IllegalStateException("Context is already attached."); throw new IllegalStateException("Context is already attached.");
} }
mContext = context; mContextRef = new WeakReference<>(context);
ContextImpl impl = ContextImpl.getImpl(mContext); final ContextImpl impl = ContextImpl.getImpl(context);
impl.setResources(impl.createWindowContextResources()); impl.setResources(impl.createWindowContextResources());
} }
@Override @Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
final int currentDisplayId = mContext.getDisplayId(); final Context context = mContextRef.get();
if (context == null) {
return;
}
final int currentDisplayId = context.getDisplayId();
final boolean displayChanged = newDisplayId != currentDisplayId; final boolean displayChanged = newDisplayId != currentDisplayId;
final Configuration config = new Configuration(mContext.getResources() final Configuration config = new Configuration(context.getResources()
.getConfiguration()); .getConfiguration());
final boolean configChanged = config.isOtherSeqNewer(newConfig) final boolean configChanged = config.isOtherSeqNewer(newConfig)
&& config.updateFrom(newConfig) != 0; && config.updateFrom(newConfig) != 0;
if (displayChanged || configChanged) { if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens // TODO(ag/9789103): update resource manager logic to track non-activity tokens
mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId, mResourcesManager.updateResourcesForActivity(this, config, newDisplayId,
displayChanged); displayChanged);
} }
if (displayChanged) { if (displayChanged) {
mContext.updateDisplay(newDisplayId); context.updateDisplay(newDisplayId);
} }
} }
@Override
public void onWindowTokenRemoved() {
final WindowContext context = mContextRef.get();
if (context != null) {
context.destroy();
mContextRef.clear();
}
// If a secondary display is detached, release all views attached to this token.
WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(),
"WindowContext");
}
} }

View File

@@ -124,13 +124,20 @@ interface IWindowManager
* @param type Window type to be used with this token. * @param type Window type to be used with this token.
* @param options A bundle used to pass window-related options. * @param options A bundle used to pass window-related options.
* @param displayId The ID of the display where this token should be added. * @param displayId The ID of the display where this token should be added.
* @param packageName The name of package to request to add window token. * @param packageName The name of package to request to add window token. Could be {@code null}
* if callers holds the MANAGE_APP_TOKENS permission.
* @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
* otherwise. * otherwise.
*/ */
int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options, int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
String packageName); String packageName);
void addWindowToken(IBinder token, int type, int displayId); void addWindowToken(IBinder token, int type, int displayId);
/**
* Remove window token on a specific display.
*
* @param token Token to be removed
* @displayId The ID of the display where this token should be removed.
*/
void removeWindowToken(IBinder token, int displayId); void removeWindowToken(IBinder token, int displayId);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent); void prepareAppTransition(int transit, boolean alwaysKeepCurrent);

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2020 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 android.app;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link WindowContext}
*
* <p>Build/Install/Run:
* atest FrameworksCoreTests:WindowContextTest
*
* <p>This test class is a part of Window Manager Service tests and specified in
* {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
*/
@FlakyTest(bugId = 150812449, detail = "Remove after confirmed it's stable.")
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class WindowContextTest {
@Test
public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
final Context instContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
final Context context = instContext.createDisplayContext(display);
final WindowContext windowContext = new WindowContext(context, TYPE_APPLICATION_OVERLAY,
null /* options */);
final IBinder token = windowContext.getActivityToken();
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
windowContext.release();
assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
}
}

View File

@@ -56,17 +56,17 @@ public class WindowMetricsTest {
@Before @Before
public void setUp() { public void setUp() {
final Context insetContext = InstrumentationRegistry.getInstrumentation() final Context instContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext(); .getTargetContext();
final Display display = insetContext.getSystemService(DisplayManager.class) final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY); .getDisplay(DEFAULT_DISPLAY);
mWindowContext = insetContext.createDisplayContext(display) mWindowContext = instContext.createDisplayContext(display)
.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */); .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
mWm = mWindowContext.getSystemService(WindowManager.class); mWm = mWindowContext.getSystemService(WindowManager.class);
} }
@Test @Test
public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() { public void testAddViewAndRemoveView_GetMetrics_DoNotCrash() {
final View view = new View(mWindowContext); final View view = new View(mWindowContext);
final WindowManager.LayoutParams params = final WindowManager.LayoutParams params =
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);

View File

@@ -1507,6 +1507,18 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS", "group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java" "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
}, },
"838570988": {
"message": "Could not report token removal to the window token client.",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowToken.java"
},
"845234215": {
"message": "App is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
"853091290": { "853091290": {
"message": "Moved stack=%s behind stack=%s", "message": "Moved stack=%s behind stack=%s",
"level": "DEBUG", "level": "DEBUG",

View File

@@ -32,6 +32,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID; import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid; import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -2573,6 +2574,8 @@ public class WindowManagerService extends IWindowManager.Stub
String packageName, boolean fromClientToken) { String packageName, boolean fromClientToken) {
final boolean callerCanManageAppTokens = final boolean callerCanManageAppTokens =
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
// WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
// by checkAddPermission.
if (!callerCanManageAppTokens) { if (!callerCanManageAppTokens) {
final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */, final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
packageName, new int[1]); packageName, new int[1]);
@@ -2587,7 +2590,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) { synchronized (mGlobalLock) {
if (!callerCanManageAppTokens) { if (!callerCanManageAppTokens) {
if (packageName == null || !unprivilegedAppCanCreateTokenWith( if (packageName == null || !unprivilegedAppCanCreateTokenWith(
null /* parentWindow */, callingUid, type, type, null /* tokenForLog */, null /* parentWindow */, callingUid, type, type, binder,
packageName)) { packageName)) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
} }
@@ -2612,7 +2615,7 @@ public class WindowManagerService extends IWindowManager.Stub
new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
} else { } else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
false /* roundedCornerOverlay */, fromClientToken); callingUid, false /* roundedCornerOverlay */, fromClientToken);
} }
} }
} finally { } finally {
@@ -2635,8 +2638,25 @@ public class WindowManagerService extends IWindowManager.Stub
@Override @Override
public void removeWindowToken(IBinder binder, int displayId) { public void removeWindowToken(IBinder binder, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { final boolean callerCanManageAppTokens =
throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
final WindowToken windowToken;
synchronized (mGlobalLock) {
windowToken = mRoot.getWindowToken(binder);
}
if (windowToken == null) {
ProtoLog.w(WM_ERROR,
"removeWindowToken: Attempted to remove non-existing token: %s", binder);
return;
}
final int callingUid = Binder.getCallingUid();
// If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
// remove the window tokens which they added themselves.
if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
|| callingUid != windowToken.getOwnerUid())) {
throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
+ " to remove token owned by another uid");
} }
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
@@ -2649,14 +2669,7 @@ public class WindowManagerService extends IWindowManager.Stub
return; return;
} }
final WindowToken token = dc.removeWindowToken(binder); dc.removeWindowToken(binder);
if (token == null) {
ProtoLog.w(WM_ERROR,
"removeWindowToken: Attempted to remove non-existing token: %s",
binder);
return;
}
dc.getInputMonitor().updateInputWindowsLw(true /*force*/); dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
} }
} finally { } finally {

View File

@@ -16,6 +16,7 @@
package com.android.server.wm; package com.android.server.wm;
import static android.os.Process.INVALID_UID;
import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -44,6 +45,7 @@ import android.graphics.Rect;
import android.os.Debug; import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Slog;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo; import android.view.DisplayInfo;
import android.view.InsetsState; import android.view.InsetsState;
@@ -106,6 +108,11 @@ class WindowToken extends WindowContainer<WindowState> {
@VisibleForTesting @VisibleForTesting
final boolean mFromClientToken; final boolean mFromClientToken;
private DeathRecipient mDeathRecipient;
private boolean mBinderDied = false;
private final int mOwnerUid;
/** /**
* Used to fix the transform of the token to be rotated to a rotation different than it's * Used to fix the transform of the token to be rotated to a rotation different than it's
* display. The window frames and surfaces corresponding to this token will be layouted and * display. The window frames and surfaces corresponding to this token will be layouted and
@@ -165,6 +172,30 @@ class WindowToken extends WindowContainer<WindowState> {
} }
} }
private class DeathRecipient implements IBinder.DeathRecipient {
private boolean mHasUnlinkToDeath = false;
@Override
public void binderDied() {
synchronized (mWmService.mGlobalLock) {
mBinderDied = true;
removeImmediately();
}
}
void linkToDeath() throws RemoteException {
token.linkToDeath(DeathRecipient.this, 0);
}
void unlinkToDeath() {
if (mHasUnlinkToDeath) {
return;
}
token.unlinkToDeath(DeathRecipient.this, 0);
mHasUnlinkToDeath = true;
}
}
/** /**
* Compares two child window of this token and returns -1 if the first is lesser than the * Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise. * second in terms of z-order and 1 otherwise.
@@ -193,23 +224,35 @@ class WindowToken extends WindowContainer<WindowState> {
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
roundedCornerOverlay, false /* fromClientToken */); roundedCornerOverlay, false /* fromClientToken */);
} }
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
boolean fromClientToken) { boolean roundedCornerOverlay, boolean fromClientToken) {
super(service); super(service);
token = _token; token = _token;
windowType = type; windowType = type;
mPersistOnEmpty = persistOnEmpty; mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens; mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay; mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken; mFromClientToken = fromClientToken;
if (dc != null) { if (dc != null) {
dc.addWindowToken(token, this); dc.addWindowToken(token, this);
} }
if (shouldReportToClient()) {
try {
mDeathRecipient = new DeathRecipient();
mDeathRecipient.linkToDeath();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
+ "display " + dc.getDisplayId(), e);
mDeathRecipient = null;
return;
}
}
} }
void removeAllWindowsIfPossible() { void removeAllWindowsIfPossible() {
@@ -222,7 +265,7 @@ class WindowToken extends WindowContainer<WindowState> {
} }
void setExiting() { void setExiting() {
if (mChildren.size() == 0) { if (isEmpty()) {
super.removeImmediately(); super.removeImmediately();
return; return;
} }
@@ -340,6 +383,21 @@ class WindowToken extends WindowContainer<WindowState> {
// Needs to occur after the token is removed from the display above to avoid attempt at // Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent. // duplicate removal of this window container from it's parent.
super.removeImmediately(); super.removeImmediately();
reportWindowTokenRemovedToClient();
}
private void reportWindowTokenRemovedToClient() {
if (!shouldReportToClient()) {
return;
}
mDeathRecipient.unlinkToDeath();
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
try {
windowTokenClient.onWindowTokenRemoved();
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
}
} }
@Override @Override
@@ -361,17 +419,9 @@ class WindowToken extends WindowContainer<WindowState> {
} }
void reportConfigToWindowTokenClient() { void reportConfigToWindowTokenClient() {
if (asActivityRecord() != null) { if (!shouldReportToClient()) {
// Activities are updated through ATM callbacks.
return; return;
} }
// Unfortunately, this WindowToken is not from WindowContext so it cannot handle
// its own configuration changes.
if (!mFromClientToken) {
return;
}
final Configuration config = getConfiguration(); final Configuration config = getConfiguration();
final int displayId = getDisplayContent().getDisplayId(); final int displayId = getDisplayContent().getDisplayId();
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
@@ -383,16 +433,26 @@ class WindowToken extends WindowContainer<WindowState> {
mLastReportedDisplay = displayId; mLastReportedDisplay = displayId;
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
if (windowTokenClient != null) { try {
try { windowTokenClient.onConfigurationChanged(config, displayId);
windowTokenClient.onConfigurationChanged(config, displayId); } catch (RemoteException e) {
} catch (RemoteException e) { ProtoLog.w(WM_ERROR,
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
"Could not report config changes to the window token client.");
}
} }
} }
/**
* @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
* registered from client side.
*/
private boolean shouldReportToClient() {
// Only report to client for WindowToken because Activities are updated through ATM
// callbacks.
return asActivityRecord() == null
// Report to {@link android.view.WindowTokenClient} if this token was registered from it.
&& mFromClientToken && !mBinderDied;
}
@Override @Override
void assignLayer(SurfaceControl.Transaction t, int layer) { void assignLayer(SurfaceControl.Transaction t, int layer) {
if (windowType == TYPE_DOCK_DIVIDER) { if (windowType == TYPE_DOCK_DIVIDER) {
@@ -616,4 +676,8 @@ class WindowToken extends WindowContainer<WindowState> {
int getWindowLayerFromType() { int getWindowLayerFromType() {
return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens); return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens);
} }
int getOwnerUid() {
return mOwnerUid;
}
} }

View File

@@ -16,13 +16,18 @@
package com.android.server.wm; package com.android.server.wm;
import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.IBinder; import android.os.IBinder;
@@ -85,4 +90,19 @@ public class WindowManagerServiceTests extends WindowTestsBase {
assertFalse(windowToken.mRoundedCornerOverlay); assertFalse(windowToken.mRoundedCornerOverlay);
assertTrue(windowToken.mFromClientToken); assertTrue(windowToken.mFromClientToken);
} }
@Test(expected = SecurityException.class)
public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
IBinder token = mock(IBinder.class);
mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
null /* options */, null /* options */);
spyOn(mWm);
when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
WindowToken windowToken = mWm.mRoot.getWindowToken(token);
spyOn(windowToken);
when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
}
} }

View File

@@ -16,6 +16,7 @@
package com.android.server.wm; package com.android.server.wm;
import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -152,7 +153,7 @@ public class WindowTokenTests extends WindowTestsBase {
token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
true /* roundedCornerOverlay */, true /* fromClientToken */); INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
assertTrue(token.mRoundedCornerOverlay); assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.mFromClientToken); assertTrue(token.mFromClientToken);
} }
@@ -166,7 +167,7 @@ public class WindowTokenTests extends WindowTestsBase {
public void testSurfaceCreatedForWindowToken() { public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService, final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */, mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
mDisplayContent, true /* ownerCanManageAppTokens */, mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
true /* roundedCornerOverlay */, true /* fromClientToken */); true /* roundedCornerOverlay */, true /* fromClientToken */);
assertNull(fromClientToken.mSurfaceControl); assertNull(fromClientToken.mSurfaceControl);
@@ -175,7 +176,7 @@ public class WindowTokenTests extends WindowTestsBase {
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService, final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */); false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl); assertNotNull(nonClientToken.mSurfaceControl);
} }

View File

@@ -17,6 +17,7 @@
package com.android.framework.permission.tests; package com.android.framework.permission.tests;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.os.Binder; import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
@@ -27,6 +28,8 @@ import android.view.IWindowManager;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.junit.Test;
/** /**
* TODO: Remove this. This is only a placeholder, need to implement this. * TODO: Remove this. This is only a placeholder, need to implement this.
*/ */
@@ -53,7 +56,7 @@ public class WindowManagerPermissionTests extends TestCase {
} }
try { try {
mWm.addWindowToken(null, 0, DEFAULT_DISPLAY); mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
fail("IWindowManager.addWindowToken did not throw SecurityException as" fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected"); + " expected");
} catch (SecurityException e) { } catch (SecurityException e) {
@@ -62,16 +65,6 @@ public class WindowManagerPermissionTests extends TestCase {
fail("Unexpected remote exception"); fail("Unexpected remote exception");
} }
try {
mWm.removeWindowToken(null, DEFAULT_DISPLAY);
fail("IWindowManager.removeWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
// expected
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
try { try {
mWm.prepareAppTransition(0, false); mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as" fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
@@ -182,4 +175,29 @@ public class WindowManagerPermissionTests extends TestCase {
fail("Unexpected remote exception"); fail("Unexpected remote exception");
} }
} }
@Test
public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
// Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
try {
mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
// expected
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
// Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
try {
mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
// expected
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
}
} }

View File

@@ -47,7 +47,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.InsetsSourceConsumerTest", "android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest", "android.view.InsetsStateTest",
"android.view.WindowMetricsTest", "android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest" "android.view.PendingInsetsControllerTest",
"android.app.WindowContextTest"
}; };
public FrameworksTestsFilter(Bundle testArgs) { public FrameworksTestsFilter(Bundle testArgs) {