summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IWindowToken.aidl2
-rw-r--r--core/java/android/app/WindowContext.java19
-rw-r--r--core/java/android/app/WindowTokenClient.java39
-rw-r--r--core/java/android/view/IWindowManager.aidl9
-rw-r--r--core/tests/coretests/src/android/app/WindowContextTest.java74
-rw-r--r--core/tests/coretests/src/android/view/WindowMetricsTest.java8
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java104
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java7
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java40
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java3
13 files changed, 309 insertions, 65 deletions
diff --git a/core/java/android/app/IWindowToken.aidl b/core/java/android/app/IWindowToken.aidl
index 8ea881fba09c..3627b0f13a7f 100644
--- a/core/java/android/app/IWindowToken.aidl
+++ b/core/java/android/app/IWindowToken.aidl
@@ -30,4 +30,6 @@ import android.view.IWindow;
*/
oneway interface IWindowToken {
void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
+
+ void onWindowTokenRemoved();
}
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 878993ebcd19..3a06c9d79fee 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -28,6 +28,8 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.ref.Reference;
/**
@@ -75,8 +77,6 @@ public class WindowContext extends ContextWrapper {
// config back to the client.
result = mWms.addWindowTokenWithOptions(
mToken, type, getDisplayId(), options, getPackageName());
-
- // TODO(window-context): remove token with a DeathObserver
} catch (RemoteException e) {
mOwnsToken = false;
throw e.rethrowFromSystemServer();
@@ -100,6 +100,13 @@ public class WindowContext extends ContextWrapper {
@Override
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) {
try {
mWms.removeWindowToken(mToken, getDisplayId());
@@ -108,6 +115,12 @@ public class WindowContext extends ContextWrapper {
throw e.rethrowFromSystemServer();
}
}
- super.finalize();
+ destroy();
+ }
+
+ void destroy() {
+ final ContextImpl impl = (ContextImpl) getBaseContext();
+ impl.scheduleFinalCleanup(getClass().getName(), "WindowContext");
+ Reference.reachabilityFence(this);
}
}
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index ed0179bb9839..301960ec53f9 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -20,6 +20,9 @@ import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
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
@@ -31,9 +34,9 @@ import android.os.IBinder;
public class WindowTokenClient extends IWindowToken.Stub {
/**
* 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();
@@ -47,30 +50,46 @@ public class WindowTokenClient extends IWindowToken.Stub {
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
*/
- void attachContext(@NonNull Context context) {
- if (mContext != null) {
+ void attachContext(@NonNull WindowContext context) {
+ if (mContextRef != null) {
throw new IllegalStateException("Context is already attached.");
}
- mContext = context;
- ContextImpl impl = ContextImpl.getImpl(mContext);
+ mContextRef = new WeakReference<>(context);
+ final ContextImpl impl = ContextImpl.getImpl(context);
impl.setResources(impl.createWindowContextResources());
}
@Override
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 Configuration config = new Configuration(mContext.getResources()
+ final Configuration config = new Configuration(context.getResources()
.getConfiguration());
final boolean configChanged = config.isOtherSeqNewer(newConfig)
&& config.updateFrom(newConfig) != 0;
if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
- mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId,
+ mResourcesManager.updateResourcesForActivity(this, config, newDisplayId,
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");
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 84ac90bae258..5648adfb78bc 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -124,13 +124,20 @@ interface IWindowManager
* @param type Window type to be used with this token.
* @param options A bundle used to pass window-related options.
* @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
* otherwise.
*/
int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
String packageName);
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 prepareAppTransition(int transit, boolean alwaysKeepCurrent);
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
new file mode 100644
index 000000000000..630e16ac80d4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -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));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
index 74524bf6d76f..ddc977d380ae 100644
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -56,17 +56,17 @@ public class WindowMetricsTest {
@Before
public void setUp() {
- final Context insetContext = InstrumentationRegistry.getInstrumentation()
+ final Context instContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
- final Display display = insetContext.getSystemService(DisplayManager.class)
+ final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
- mWindowContext = insetContext.createDisplayContext(display)
+ mWindowContext = instContext.createDisplayContext(display)
.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
mWm = mWindowContext.getSystemService(WindowManager.class);
}
@Test
- public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
+ public void testAddViewAndRemoveView_GetMetrics_DoNotCrash() {
final View view = new View(mWindowContext);
final WindowManager.LayoutParams params =
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a871047a0602..72998ad0d2d7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1525,6 +1525,18 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"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": {
"message": "Moved stack=%s behind stack=%s",
"level": "DEBUG",
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b1f22f8623d6..0674133bcdb7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -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_PC;
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.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -2558,6 +2559,8 @@ public class WindowManagerService extends IWindowManager.Stub
String packageName, boolean fromClientToken) {
final boolean callerCanManageAppTokens =
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
+ // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
+ // by checkAddPermission.
if (!callerCanManageAppTokens) {
final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
packageName, new int[1]);
@@ -2572,7 +2575,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
if (!callerCanManageAppTokens) {
if (packageName == null || !unprivilegedAppCanCreateTokenWith(
- null /* parentWindow */, callingUid, type, type, null /* tokenForLog */,
+ null /* parentWindow */, callingUid, type, type, binder,
packageName)) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -2597,7 +2600,7 @@ public class WindowManagerService extends IWindowManager.Stub
new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
} else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- false /* roundedCornerOverlay */, fromClientToken);
+ callingUid, false /* roundedCornerOverlay */, fromClientToken);
}
}
} finally {
@@ -2620,8 +2623,25 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void removeWindowToken(IBinder binder, int displayId) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ final boolean callerCanManageAppTokens =
+ 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();
@@ -2634,14 +2654,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- final WindowToken token = dc.removeWindowToken(binder);
- if (token == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s",
- binder);
- return;
- }
-
+ dc.removeWindowToken(binder);
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
} finally {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 3c2b6ec9711d..ccf51216e8d3 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -44,6 +45,7 @@ import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.InsetsState;
@@ -106,6 +108,11 @@ class WindowToken extends WindowContainer<WindowState> {
@VisibleForTesting
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
* 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
* 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,
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 */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
- boolean fromClientToken) {
+ DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
+ boolean roundedCornerOverlay, boolean fromClientToken) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+ mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
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() {
@@ -222,7 +265,7 @@ class WindowToken extends WindowContainer<WindowState> {
}
void setExiting() {
- if (mChildren.size() == 0) {
+ if (isEmpty()) {
super.removeImmediately();
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
// duplicate removal of this window container from it's parent.
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
@@ -361,17 +419,9 @@ class WindowToken extends WindowContainer<WindowState> {
}
void reportConfigToWindowTokenClient() {
- if (asActivityRecord() != null) {
- // Activities are updated through ATM callbacks.
+ if (!shouldReportToClient()) {
return;
}
-
- // Unfortunately, this WindowToken is not from WindowContext so it cannot handle
- // its own configuration changes.
- if (!mFromClientToken) {
- return;
- }
-
final Configuration config = getConfiguration();
final int displayId = getDisplayContent().getDisplayId();
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
@@ -383,16 +433,26 @@ class WindowToken extends WindowContainer<WindowState> {
mLastReportedDisplay = displayId;
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- if (windowTokenClient != null) {
- try {
- windowTokenClient.onConfigurationChanged(config, displayId);
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR,
- "Could not report config changes to the window token client.");
- }
+ try {
+ windowTokenClient.onConfigurationChanged(config, displayId);
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR,
+ "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
void assignLayer(SurfaceControl.Transaction t, int layer) {
if (windowType == TYPE_DOCK_DIVIDER) {
@@ -615,4 +675,8 @@ class WindowToken extends WindowContainer<WindowState> {
int getWindowLayerFromType() {
return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens);
}
+
+ int getOwnerUid() {
+ return mOwnerUid;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index da4bde59a09e..79b9ae1b902a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -16,13 +16,18 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
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.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -85,4 +90,19 @@ public class WindowManagerServiceTests extends WindowTestsBase {
assertFalse(windowToken.mRoundedCornerOverlay);
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());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 7a347cb050be..76479cb95b09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,6 +16,7 @@
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.TYPE_APPLICATION;
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,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
- true /* roundedCornerOverlay */, true /* fromClientToken */);
+ INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.mFromClientToken);
}
@@ -166,7 +167,7 @@ public class WindowTokenTests extends WindowTestsBase {
public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
- mDisplayContent, true /* ownerCanManageAppTokens */,
+ mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
true /* roundedCornerOverlay */, true /* fromClientToken */);
assertNull(fromClientToken.mSurfaceControl);
@@ -175,7 +176,7 @@ public class WindowTokenTests extends WindowTestsBase {
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl);
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 4c2a984f8198..737665fb97e4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -17,6 +17,7 @@
package com.android.framework.permission.tests;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.os.Binder;
import android.os.RemoteException;
@@ -27,6 +28,8 @@ import android.view.IWindowManager;
import junit.framework.TestCase;
+import org.junit.Test;
+
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
*/
@@ -53,7 +56,7 @@ public class WindowManagerPermissionTests extends TestCase {
}
try {
- mWm.addWindowToken(null, 0, DEFAULT_DISPLAY);
+ mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -63,16 +66,6 @@ public class WindowManagerPermissionTests extends TestCase {
}
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 {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
@@ -182,4 +175,29 @@ public class WindowManagerPermissionTests extends TestCase {
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");
+ }
+ }
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 3026e0b51133..0dd45bad8a01 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -47,7 +47,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
"android.view.WindowMetricsTest",
- "android.view.PendingInsetsControllerTest"
+ "android.view.PendingInsetsControllerTest",
+ "android.app.WindowContextTest"
};
public FrameworksTestsFilter(Bundle testArgs) {