Handle config/display changes for WindowContext
Introduce IWindowToken to report config/display changes from server side. When config change callback is received, it will update the resources associated with the window token. Test: WindowContextTests Bug: 128338354 Bug: 146820733 Change-Id: I871bd78a21dbde1286786e65c340b6259b873660
This commit is contained in:
committed by
Charles Chen
parent
80c2e07e00
commit
02c0e4d2ac
@@ -2409,17 +2409,35 @@ class ContextImpl extends Context {
|
||||
+ "other visual contexts, such as Activity or one created with "
|
||||
+ "Context#createDisplayContext(Display)");
|
||||
}
|
||||
return new WindowContext(this, null /* token */, type, options);
|
||||
return new WindowContext(this, type, options);
|
||||
}
|
||||
|
||||
ContextImpl createBaseWindowContext(IBinder token) {
|
||||
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
|
||||
mSplitName, token, mUser, mFlags, mClassLoader, null);
|
||||
context.mIsUiContext = true;
|
||||
|
||||
context.mIsAssociatedWithDisplay = true;
|
||||
return context;
|
||||
}
|
||||
|
||||
Resources createWindowContextResources() {
|
||||
final String resDir = mPackageInfo.getResDir();
|
||||
final String[] splitResDirs = mPackageInfo.getSplitResDirs();
|
||||
final String[] overlayDirs = mPackageInfo.getOverlayDirs();
|
||||
final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles;
|
||||
final int displayId = getDisplayId();
|
||||
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
|
||||
? mPackageInfo.getCompatibilityInfo()
|
||||
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
|
||||
final List<ResourcesLoader> loaders = mResources.getLoaders();
|
||||
|
||||
// TODO(b/128338354): Rename to createTokenResources
|
||||
return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs,
|
||||
overlayDirs, libDirs, displayId, null /* overrideConfig */,
|
||||
compatInfo, mClassLoader, loaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Context createFeatureContext(@Nullable String featureId) {
|
||||
return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
|
||||
|
||||
33
core/java/android/app/IWindowToken.aidl
Normal file
33
core/java/android/app/IWindowToken.aidl
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
** Copyright 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 android.content.res.Configuration;
|
||||
import android.view.IWindow;
|
||||
|
||||
/**
|
||||
* Callback to receive configuration changes from {@link com.android.server.WindowToken}.
|
||||
* WindowToken can be regarded to as a group of {@link android.view.IWindow} added from the same
|
||||
* visual context, such as {@link Activity} or one created with
|
||||
* {@link android.content.Context#createWindowContext(int)}. When WindowToken receives configuration
|
||||
* changes and/or when it is moved between displays, it will propagate the changes to client side
|
||||
* via this interface.
|
||||
* @see android.content.Context#createWindowContext(int)
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IWindowToken {
|
||||
void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
|
||||
}
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import static android.view.WindowManagerGlobal.ADD_OKAY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -26,73 +28,62 @@ import android.view.IWindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
/**
|
||||
* {@link WindowContext} is a context for non-activity windows such as
|
||||
* {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system
|
||||
* windows. Its resources and configuration are adjusted to the area of the display that will be
|
||||
* used when a new window is added via {@link android.view.WindowManager.addView}.
|
||||
* used when a new window is added via {@link android.view.WindowManager#addView}.
|
||||
*
|
||||
* @see Context#createWindowContext(int, Bundle)
|
||||
* @hide
|
||||
*/
|
||||
// TODO(b/128338354): Handle config/display changes from server side.
|
||||
public class WindowContext extends ContextWrapper {
|
||||
private final WindowManagerImpl mWindowManager;
|
||||
private final IWindowManager mWms;
|
||||
private final IBinder mToken;
|
||||
private final int mDisplayId;
|
||||
private final WindowTokenClient mToken;
|
||||
private boolean mOwnsToken;
|
||||
|
||||
/**
|
||||
* Default constructor. Can either accept an existing token or generate one and registers it
|
||||
* with the server if necessary.
|
||||
* Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
|
||||
* the token.
|
||||
*
|
||||
* @param base Base {@link Context} for this new instance.
|
||||
* @param token A valid {@link com.android.server.wm.WindowToken}. Pass {@code null} to generate
|
||||
* one.
|
||||
* @param type Window type to be used with this context.
|
||||
* @hide
|
||||
*/
|
||||
public WindowContext(Context base, IBinder token, int type, Bundle options) {
|
||||
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
|
||||
// Correct base context will be built once the token is resolved, so passing 'null' here.
|
||||
super(null /* base */);
|
||||
|
||||
mWms = WindowManagerGlobal.getWindowManagerService();
|
||||
if (token != null && !isWindowToken(token)) {
|
||||
throw new IllegalArgumentException("Token must be registered to server.");
|
||||
}
|
||||
mToken = token != null ? token : new Binder();
|
||||
mToken = new WindowTokenClient();
|
||||
|
||||
|
||||
final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
|
||||
attachBaseContext(contextImpl);
|
||||
contextImpl.setOuterContext(this);
|
||||
|
||||
mDisplayId = getDisplayId();
|
||||
mToken.attachContext(this);
|
||||
|
||||
mWindowManager = new WindowManagerImpl(this);
|
||||
mWindowManager.setDefaultToken(mToken);
|
||||
|
||||
// TODO(b/128338354): Obtain the correct config from WM and adjust resources.
|
||||
if (token != null) {
|
||||
mOwnsToken = false;
|
||||
return;
|
||||
}
|
||||
int result;
|
||||
try {
|
||||
mWms.addWindowTokenWithOptions(mToken, type, mDisplayId, options, getPackageName());
|
||||
// Register the token with WindowManager. This will also call back with the current
|
||||
// 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();
|
||||
}
|
||||
mOwnsToken = true;
|
||||
}
|
||||
|
||||
/** Check if the passed window token is registered with the server. */
|
||||
private boolean isWindowToken(@NonNull IBinder token) {
|
||||
try {
|
||||
return mWms.isWindowToken(token);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
return false;
|
||||
mOwnsToken = result == ADD_OKAY;
|
||||
Reference.reachabilityFence(this);
|
||||
}
|
||||
|
||||
private static ContextImpl createBaseWindowContext(Context outer, IBinder token) {
|
||||
@@ -112,7 +103,7 @@ public class WindowContext extends ContextWrapper {
|
||||
protected void finalize() throws Throwable {
|
||||
if (mOwnsToken) {
|
||||
try {
|
||||
mWms.removeWindowToken(mToken, mDisplayId);
|
||||
mWms.removeWindowToken(mToken, getDisplayId());
|
||||
mOwnsToken = false;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
|
||||
76
core/java/android/app/WindowTokenClient.java
Normal file
76
core/java/android/app/WindowTokenClient.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from
|
||||
* server when window token config is updated or when it is moved between displays, and update the
|
||||
* resources associated with this token on the client side. This will make sure that
|
||||
* {@link WindowContext} instances will have updated resources and configuration.
|
||||
* @hide
|
||||
*/
|
||||
public class WindowTokenClient extends IWindowToken.Stub {
|
||||
/**
|
||||
* Attached {@link Context} for this window token to update configuration and resources.
|
||||
* Initialized by {@link #attachContext(Context)}.
|
||||
*/
|
||||
private Context mContext = null;
|
||||
|
||||
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
|
||||
|
||||
/**
|
||||
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
|
||||
* can only attach one {@link Context}.
|
||||
* <p>This method must be called before invoking
|
||||
* {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle,
|
||||
* String)}.<p/>
|
||||
*
|
||||
* @param context context to be attached
|
||||
* @throws IllegalStateException if attached context has already existed.
|
||||
*/
|
||||
void attachContext(@NonNull Context context) {
|
||||
if (mContext != null) {
|
||||
throw new IllegalStateException("Context is already attached.");
|
||||
}
|
||||
mContext = context;
|
||||
ContextImpl impl = ContextImpl.getImpl(mContext);
|
||||
impl.setResources(impl.createWindowContextResources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
|
||||
final int currentDisplayId = mContext.getDisplayId();
|
||||
final boolean displayChanged = newDisplayId != currentDisplayId;
|
||||
final Configuration config = new Configuration(mContext.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,
|
||||
displayChanged);
|
||||
}
|
||||
if (displayChanged) {
|
||||
mContext.updateDisplay(newDisplayId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,7 +458,7 @@ public interface WindowManager extends ViewManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largets {@link WindowMetrics} an app may expect in the current system state.
|
||||
* Returns the largest {@link WindowMetrics} an app may expect in the current system state.
|
||||
* <p>
|
||||
* The metrics describe the size of the largest potential area the window might occupy with
|
||||
* {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
|
||||
|
||||
@@ -709,6 +709,18 @@
|
||||
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
|
||||
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
|
||||
},
|
||||
"-650040763": {
|
||||
"message": "rotationForOrientation(orient=%d, last=%d); user=%d %s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_ORIENTATION",
|
||||
"at": "com\/android\/server\/wm\/DisplayRotation.java"
|
||||
},
|
||||
"-639305784": {
|
||||
"message": "Could not report config changes to the window token client.",
|
||||
"level": "WARN",
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowToken.java"
|
||||
},
|
||||
"-635082269": {
|
||||
"message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b",
|
||||
"level": "INFO",
|
||||
|
||||
@@ -2583,11 +2583,18 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
@Override
|
||||
public void addWindowToken(IBinder binder, int type, int displayId) {
|
||||
addWindowTokenWithOptions(binder, type, displayId, null /* options */,
|
||||
null /* packageName */);
|
||||
null /* packageName */, false /* fromClientToken */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
|
||||
String packageName) {
|
||||
return addWindowTokenWithOptions(binder, type, displayId, options, packageName,
|
||||
true /* fromClientToken */);
|
||||
}
|
||||
|
||||
private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
|
||||
String packageName, boolean fromClientToken) {
|
||||
final boolean callerCanManageAppTokens =
|
||||
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
|
||||
if (!callerCanManageAppTokens) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
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;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
@@ -23,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
|
||||
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
|
||||
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
|
||||
import static com.android.server.wm.ProtoLogGroup.WM_ERROR;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
|
||||
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
|
||||
@@ -36,10 +38,12 @@ import static com.android.server.wm.WindowTokenProto.WINDOWS;
|
||||
import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.app.IWindowToken;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.InsetsState;
|
||||
@@ -91,6 +95,11 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
|
||||
private FixedRotationTransformState mFixedRotationTransformState;
|
||||
|
||||
private Configuration mLastReportedConfig;
|
||||
private int mLastReportedDisplay = INVALID_DISPLAY;
|
||||
|
||||
private final boolean mFromClientToken;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -167,13 +176,21 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
}
|
||||
|
||||
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
|
||||
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
|
||||
DisplayContent dc, boolean ownerCanManageAppTokens, boolean fromClientToken) {
|
||||
this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
|
||||
false /* roundedCornersOverlay */, fromClientToken);
|
||||
}
|
||||
|
||||
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
|
||||
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
|
||||
boolean fromClientToken) {
|
||||
super(service);
|
||||
token = _token;
|
||||
windowType = type;
|
||||
mPersistOnEmpty = persistOnEmpty;
|
||||
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
|
||||
mRoundedCornerOverlay = roundedCornerOverlay;
|
||||
mFromClientToken = fromClientToken;
|
||||
if (dc != null) {
|
||||
dc.addWindowToken(token, this);
|
||||
}
|
||||
@@ -305,8 +322,47 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
// up with goodToGo, so we don't move a window
|
||||
// to another display before the window behind
|
||||
// it is ready.
|
||||
|
||||
super.onDisplayChanged(dc);
|
||||
reportConfigToWindowTokenClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newParentConfig) {
|
||||
super.onConfigurationChanged(newParentConfig);
|
||||
reportConfigToWindowTokenClient();
|
||||
}
|
||||
|
||||
void reportConfigToWindowTokenClient() {
|
||||
if (asActivityRecord() != null) {
|
||||
// Activities are updated through ATM callbacks.
|
||||
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) {
|
||||
// No changes since last reported time.
|
||||
return;
|
||||
}
|
||||
|
||||
mLastReportedConfig = config;
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user