Merge "Fix WindowContext leak" into rvc-dev am: 5c082c998f am: c4750c036b
Change-Id: Ibb466329ba10989a7e1bda77c484ed3b68bfe85e
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
74
core/tests/coretests/src/android/app/WindowContextTest.java
Normal file
74
core/tests/coretests/src/android/app/WindowContextTest.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user