Make process level rotated config and display metrics consistent

The application may get Resources instance from Resources.getSystem()
and context.getApplicationContext().getResources(). Since fixed
rotation is introduced that allows an activity to start in a different
rotation than the current display, when using getConfiguration() and
getDisplayMetrics() of these Resources instances, the orientation
and metrics need to be the same as current display is rotated.
Otherwise the app may show unexpected UI layout.

Although it is not recommended to use global resources/config for
activity. One of the goal of fixed rotation transform is to simulate
the app is started in a rotated environment, so this CL makes the
configuration and display metrics of system resources are consistent
with application and activity for compatibility.

About WindowProcessController and ActivityStackSupervisor:
 The process configuration passed to LaunchActivityItem may be
 associated from activity. if the sequence number of configuration
 is overridden by activity, the configuration may be ignored when
 launching the activity because the sequence number isn't larger
 than the previous process configuration. Although there will be a
 ConfigurationChangeItem later to update correct state, the app may
 get the intermediate state with old configuration and metrics.

About ResourcesManager and DisplayAdjustments:
 There are 2 new fields appWidth and appHeight added to
 DisplayAdjustments#FixedRotationAdjustments because the display
 metrics from Resources.getSystem() is independent from activity
 configuration. Only window manager knows the rotated size, so
 the values need to send to client and then ResourcesManager takes
 the adjustment to change the global display metrics.

About WindowToken:
 When fixed rotation is applied on the token, send the
 FixedRotationAdjustmentsItem first so the later configuration
 change can pick the adjustment at ActivityThread. And because the
 registration of activity configuration only occurs on add/remove
 activity, if it is only switching to another existing activity in
 different orientation, the process configuration still needs to
 be updated.

About ActivityThread:
 The code flow for a rotated activity (DA = display adjustments):
 - Launch new activity
    handleLaunchActivity: override app DA
     handleConfigurationChanged: adjust global display metrics by DA
     performLaunchActivity
      createBaseContextForActivity: override activity DA
 - Resume existing activity
    handleFixedRotationAdjustments: override app and activity DA
    handleConfigurationChanged: adjust global display metrics by DA
    handleResumeActivity

Also some minor corrections:
- Set missing rotated max bounds.
- Fix wrong display metrics adjustment that xdpi and ydpi should
  not be swapped because they are physical attributes.

Bug: 167564038
Test: atest DisplayAdjustmentsTests
      AppConfigurationTests#testRotatedInfoWithFixedRotationTransform
      WindowProcessControllerTests#testProcessLevelConfiguration
      DisplayContenTests#testApplyTopFixedRotationTransform

Change-Id: I60bedc7e09f54683d5e857ccc51402d5d144cd9e
This commit is contained in:
Riddle Hsu
2020-09-14 22:39:16 +08:00
parent a67c5344ac
commit d5c5a1b421
11 changed files with 234 additions and 115 deletions

View File

@@ -3271,12 +3271,6 @@ public final class ActivityThread extends ClientTransactionHandler {
sendMessage(H.CLEAN_UP_CONTEXT, cci);
}
@Override
public void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments) {
handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */);
}
/**
* Applies the rotation adjustments to override display information in resources belong to the
* provided token. If the token is activity token, the adjustments also apply to application
@@ -3286,51 +3280,39 @@ public final class ActivityThread extends ClientTransactionHandler {
* @param fixedRotationAdjustments The information to override the display adjustments of
* corresponding resources. If it is null, the exiting override
* will be cleared.
* @param overrideConfig The override configuration of activity. It is used to override
* application configuration. If it is non-null, it means the token is
* confirmed as activity token. Especially when launching new activity,
* {@link #mActivities} hasn't put the new token.
*/
private void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments,
@Nullable Configuration overrideConfig) {
// The element of application configuration override is set only if the application
// adjustments are needed, because activity already has its own override configuration.
final Configuration[] appConfigOverride;
final Consumer<DisplayAdjustments> override;
if (fixedRotationAdjustments != null) {
appConfigOverride = new Configuration[1];
override = displayAdjustments -> {
displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
if (appConfigOverride[0] != null) {
displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]);
}
};
} else {
appConfigOverride = null;
override = null;
}
@Override
public void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments) {
final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
? displayAdjustments -> displayAdjustments
.setFixedRotationAdjustments(fixedRotationAdjustments)
: null;
if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
// No resources are associated with the token.
return;
}
if (overrideConfig == null) {
final ActivityClientRecord r = mActivities.get(token);
if (r == null) {
// It is not an activity token. Nothing to do for application.
return;
}
overrideConfig = r.overrideConfig;
}
if (appConfigOverride != null) {
appConfigOverride[0] = overrideConfig;
if (mActivities.get(token) == null) {
// Nothing to do for application if it is not an activity token.
return;
}
// Apply the last override to application resources for compatibility. Because the Resources
// of Display can be from application, e.g.
// applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
// and the deprecated usage:
// applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
overrideApplicationDisplayAdjustments(token, override);
}
/**
* Applies the last override to application resources for compatibility. Because the Resources
* of Display can be from application, e.g.
* applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
* and the deprecated usage:
* applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
*
* @param token The owner and target of the override.
* @param override The display adjustments override for application resources. If it is null,
* the override of the token will be removed and pop the last one to use.
*/
private void overrideApplicationDisplayAdjustments(@NonNull IBinder token,
@Nullable Consumer<DisplayAdjustments> override) {
final Consumer<DisplayAdjustments> appOverride;
if (mActiveRotationAdjustments == null) {
mActiveRotationAdjustments = new ArrayList<>(2);
@@ -3559,8 +3541,13 @@ public final class ActivityThread extends ClientTransactionHandler {
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
r.overrideConfig);
// The adjustments should have been set by handleLaunchActivity, so the last one is the
// override for activity resources.
if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
mResourcesManager.overrideTokenDisplayAdjustments(r.token,
mActiveRotationAdjustments.get(
mActiveRotationAdjustments.size() - 1).second);
}
r.mPendingFixedRotationAdjustments = null;
}
@@ -3599,6 +3586,13 @@ public final class ActivityThread extends ClientTransactionHandler {
mProfiler.startProfiling();
}
if (r.mPendingFixedRotationAdjustments != null) {
// The rotation adjustments must be applied before handling configuration, so process
// level display metrics can be adjusted.
overrideApplicationDisplayAdjustments(r.token, adjustments ->
adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
@@ -5735,7 +5729,15 @@ public final class ActivityThread extends ClientTransactionHandler {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
final Resources appResources = mInitialApplication.getResources();
if (appResources.hasOverrideDisplayAdjustments()) {
// The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
// but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
// configuration also needs to set to the adjustments for consistency.
appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
}
mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
appResources.getDisplayAdjustments());
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
@@ -7359,7 +7361,8 @@ public final class ActivityThread extends ClientTransactionHandler {
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
null /* compat */)) {
null /* compat */,
mInitialApplication.getResources().getDisplayAdjustments())) {
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());

View File

@@ -1262,12 +1262,18 @@ public class ResourcesManager {
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
synchronized(this) {
return applyConfigurationToResourcesLocked(config, compat);
return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
}
}
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
@Nullable CompatibilityInfo compat) {
return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
}
/** Applies the global configuration to the managed resources. */
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyConfigurationToResourcesLocked");
@@ -1291,6 +1297,11 @@ public class ResourcesManager {
}
DisplayMetrics displayMetrics = getDisplayMetrics();
if (adjustments != null) {
// Currently the only case where the adjustment takes effect is to simulate placing
// an app in a rotated display.
adjustments.adjustGlobalAppMetrics(displayMetrics);
}
Resources.updateSystemConfiguration(config, displayMetrics, compat);
ApplicationPackageManager.configurationChanged();

View File

@@ -130,14 +130,16 @@ public class DisplayAdjustments {
w = metrics.noncompatWidthPixels;
metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
metrics.noncompatHeightPixels = w;
}
float x = metrics.xdpi;
metrics.xdpi = metrics.ydpi;
metrics.ydpi = x;
x = metrics.noncompatXdpi;
metrics.noncompatXdpi = metrics.noncompatYdpi;
metrics.noncompatYdpi = x;
/** Adjusts global display metrics that is available to applications. */
public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) {
final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
if (rotationAdjustments == null) {
return;
}
metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth;
metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight;
}
/** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
@@ -178,7 +180,7 @@ public class DisplayAdjustments {
/**
* An application can be launched in different rotation than the real display. This class
* provides the information to adjust the values returned by {@link #Display}.
* provides the information to adjust the values returned by {@link Display}.
* @hide
*/
public static class FixedRotationAdjustments implements Parcelable {
@@ -186,12 +188,24 @@ public class DisplayAdjustments {
@Surface.Rotation
final int mRotation;
/**
* The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according
* to rotation because it minus the region of screen decorations.
*/
final int mAppWidth;
/** The rotated {@link DisplayInfo#appHeight}. */
final int mAppHeight;
/** Non-null if the device has cutout. */
@Nullable
final DisplayCutout mRotatedDisplayCutout;
public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) {
public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight,
DisplayCutout cutout) {
mRotation = rotation;
mAppWidth = appWidth;
mAppHeight = appHeight;
mRotatedDisplayCutout = cutout;
}
@@ -199,6 +213,8 @@ public class DisplayAdjustments {
public int hashCode() {
int hash = 17;
hash = hash * 31 + mRotation;
hash = hash * 31 + mAppWidth;
hash = hash * 31 + mAppHeight;
hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
return hash;
}
@@ -210,12 +226,14 @@ public class DisplayAdjustments {
}
final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
return mRotation == other.mRotation
&& mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight
&& Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
}
@Override
public String toString() {
return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
+ " appWidth=" + mAppWidth + " appHeight=" + mAppHeight
+ " cutout=" + mRotatedDisplayCutout + "}";
}
@@ -227,12 +245,16 @@ public class DisplayAdjustments {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mRotation);
dest.writeInt(mAppWidth);
dest.writeInt(mAppHeight);
dest.writeTypedObject(
new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
}
private FixedRotationAdjustments(Parcel in) {
mRotation = in.readInt();
mAppWidth = in.readInt();
mAppHeight = in.readInt();
final DisplayCutout.ParcelableWrapper cutoutWrapper =
in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;

View File

@@ -192,7 +192,7 @@ public class TransactionParcelTests {
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments(
Surface.ROTATION_90, DisplayCutout.NO_CUTOUT);
Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT);
LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo,
config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
@@ -352,7 +352,8 @@ public class TransactionParcelTests {
ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(),
null /* activityToken */);
transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(),
new FixedRotationAdjustments(Surface.ROTATION_270, DisplayCutout.NO_CUTOUT)));
new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080,
DisplayCutout.NO_CUTOUT)));
writeAndPrepareForReading(transaction);

View File

@@ -77,8 +77,10 @@ public class DisplayAdjustmentsTests {
final int realRotation = Surface.ROTATION_0;
final int fixedRotation = Surface.ROTATION_90;
mDisplayAdjustments.setFixedRotationAdjustments(
new FixedRotationAdjustments(fixedRotation, null /* cutout */));
final int appWidth = 1080;
final int appHeight = 1920;
mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments(
fixedRotation, appWidth, appHeight, null /* cutout */));
final int w = 1000;
final int h = 2000;
@@ -95,13 +97,21 @@ public class DisplayAdjustmentsTests {
metrics.heightPixels = metrics.noncompatHeightPixels = h;
final DisplayMetrics flippedMetrics = new DisplayMetrics();
flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h;
// The physical dpi should not be adjusted.
flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w;
flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w;
flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h;
flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;
mDisplayAdjustments.adjustMetrics(metrics, realRotation);
assertEquals(flippedMetrics, metrics);
mDisplayAdjustments.adjustGlobalAppMetrics(metrics);
assertEquals(appWidth, metrics.widthPixels);
assertEquals(appWidth, metrics.noncompatWidthPixels);
assertEquals(appHeight, metrics.heightPixels);
assertEquals(appHeight, metrics.noncompatHeightPixels);
}
}

View File

@@ -731,6 +731,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final Task rootTask = task.getRootTask();
beginDeferResume();
// The LaunchActivityItem also contains process configuration, so the configuration change
// from WindowProcessController#setProcess can be deferred. The major reason is that if
// the activity has FixedRotationAdjustments, it needs to be applied with configuration.
// In general, this reduces a binder transaction if process configuration is changed.
proc.pauseConfigurationDispatch();
try {
r.startFreezingScreenLocked(proc, 0);
@@ -824,9 +829,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
proc.getConfiguration(), r.getMergedOverrideConfiguration());
procConfig, r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.getSavedState());
@@ -860,6 +865,11 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
// If the seq is increased, there should be something changed (e.g. registered
// activity configuration).
proc.setLastReportedConfiguration(procConfig);
}
if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Note that the package manager will ensure
@@ -894,6 +904,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
} finally {
endDeferResume();
proc.resumeConfigurationDispatch();
}
r.launchFailed = false;

View File

@@ -1844,7 +1844,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
outConfig.windowConfiguration.getBounds().set(0, 0, dw, dh);
outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);
outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());
final int uiMode = getConfiguration().uiMode;
final DisplayCutout displayCutout =

View File

@@ -195,13 +195,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration = new Configuration();
// Configuration that is waiting to be dispatched to the process.
private Configuration mPendingConfiguration;
/** Whether the process configuration is waiting to be dispatched to the process. */
private boolean mHasPendingConfigurationChange;
// Registered display id as a listener to override config change
private int mDisplayId;
private ActivityRecord mConfigActivityRecord;
// Whether the activity config override is allowed for this process.
private volatile boolean mIsActivityConfigOverrideAllowed = true;
/** Non-zero to pause dispatching process configuration change. */
private int mPauseConfigurationDispatchCount;
/**
* Activities that hosts some UI drawn by the current process. The activities live
* in another process. This is used to check if the process is currently showing anything
@@ -1242,8 +1245,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
onMergedOverrideConfigurationChanged(Configuration.EMPTY);
}
private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
if (activityRecord == null || activityRecord.containsListener(this)) {
void registerActivityConfigurationListener(ActivityRecord activityRecord) {
if (activityRecord == null || activityRecord.containsListener(this)
// Check for the caller from outside of this class.
|| !mIsActivityConfigOverrideAllowed) {
return;
}
// A process can only register to one activityRecord to listen to the override configuration
@@ -1295,25 +1300,25 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@Override
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
super.onRequestedOverrideConfigurationChanged(
sanitizeProcessConfiguration(overrideConfiguration));
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
}
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
super.onRequestedOverrideConfigurationChanged(
sanitizeProcessConfiguration(mergedOverrideConfig));
super.onRequestedOverrideConfigurationChanged(mergedOverrideConfig);
}
private static Configuration sanitizeProcessConfiguration(Configuration config) {
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
super.resolveOverrideConfiguration(newParentConfig);
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
// Make sure that we don't accidentally override the activity type.
if (config.windowConfiguration.getActivityType() != ACTIVITY_TYPE_UNDEFINED) {
final Configuration sanitizedConfig = new Configuration(config);
sanitizedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
return sanitizedConfig;
}
return config;
resolvedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
// Activity has an independent ActivityRecord#mConfigurationSeq. If this process registers
// activity configuration, its config seq shouldn't go backwards by activity configuration.
// Otherwise if other places send wpc.getConfiguration() to client, the configuration may
// be ignored due to the seq is older.
resolvedConfig.seq = newParentConfig.seq;
}
private void updateConfiguration() {
@@ -1331,11 +1336,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (mListener.isCached()) {
// This process is in a cached state. We will delay delivering the config change to the
// process until the process is no longer cached.
if (mPendingConfiguration == null) {
mPendingConfiguration = new Configuration(config);
} else {
mPendingConfiguration.setTo(config);
}
mHasPendingConfigurationChange = true;
return;
}
@@ -1343,6 +1344,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
private void dispatchConfigurationChange(Configuration config) {
if (mPauseConfigurationDispatchCount > 0) {
mHasPendingConfigurationChange = true;
return;
}
mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
@@ -1368,7 +1374,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
private void setLastReportedConfiguration(Configuration config) {
void setLastReportedConfiguration(Configuration config) {
mLastReportedConfiguration.setTo(config);
}
@@ -1376,6 +1382,30 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mLastReportedConfiguration;
}
void pauseConfigurationDispatch() {
mPauseConfigurationDispatchCount++;
}
void resumeConfigurationDispatch() {
mPauseConfigurationDispatchCount--;
}
/**
* This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
* The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
* is newer.
*/
Configuration prepareConfigurationForLaunchingActivity() {
final Configuration config = getConfiguration();
if (mHasPendingConfigurationChange) {
mHasPendingConfigurationChange = false;
// The global configuration may not change, so the client process may have the same
// config seq. This increment ensures that the client won't ignore the configuration.
config.seq = mAtm.increaseConfigurationSeqLocked();
}
return config;
}
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
public long getCpuTime() {
return (mListener != null) ? mListener.getCpuTime() : 0;
@@ -1467,10 +1497,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
public void onProcCachedStateChanged(boolean isCached) {
if (!isCached) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
if (mPendingConfiguration != null) {
final Configuration config = mPendingConfiguration;
mPendingConfiguration = null;
dispatchConfigurationChange(config);
if (mHasPendingConfigurationChange) {
dispatchConfigurationChange(getConfiguration());
}
}
}

View File

@@ -181,6 +181,12 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
}
/** The state may not only be used by self. Make sure to leave the influence by others. */
void disassociate(WindowToken token) {
mAssociatedTokens.remove(token);
mRotatedContainers.remove(token);
}
}
private class DeathRecipient implements IBinder.DeathRecipient {
@@ -548,7 +554,7 @@ class WindowToken extends WindowContainer<WindowState> {
void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
Configuration config) {
if (mFixedRotationTransformState != null) {
cleanUpFixedRotationTransformState(true /* replacing */);
mFixedRotationTransformState.disassociate(this);
}
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
new Configuration(config), mDisplayContent.getRotation());
@@ -556,8 +562,7 @@ class WindowToken extends WindowContainer<WindowState> {
mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
mFixedRotationTransformState.mInsetsState,
mFixedRotationTransformState.mBarContentFrames);
onConfigurationChanged(getParent().getConfiguration());
notifyFixedRotationTransform(true /* enabled */);
onFixedRotationStatePrepared();
}
/**
@@ -570,12 +575,29 @@ class WindowToken extends WindowContainer<WindowState> {
return;
}
if (mFixedRotationTransformState != null) {
cleanUpFixedRotationTransformState(true /* replacing */);
mFixedRotationTransformState.disassociate(this);
}
mFixedRotationTransformState = fixedRotationState;
fixedRotationState.mAssociatedTokens.add(this);
onConfigurationChanged(getParent().getConfiguration());
onFixedRotationStatePrepared();
}
/**
* Makes the rotated states take effect for this window container and its client process.
* This should only be called when {@link #mFixedRotationTransformState} is non-null.
*/
private void onFixedRotationStatePrepared() {
// Send the adjustment info first so when the client receives configuration change, it can
// get the rotated display metrics.
notifyFixedRotationTransform(true /* enabled */);
// Resolve the rotated configuration.
onConfigurationChanged(getParent().getConfiguration());
final ActivityRecord r = asActivityRecord();
if (r != null && r.hasProcess()) {
// The application needs to be configured as in a rotated environment for compatibility.
// This registration will send the rotated configuration to its process.
r.app.registerActivityConfigurationListener(r);
}
}
/**
@@ -626,21 +648,12 @@ class WindowToken extends WindowContainer<WindowState> {
// The state is cleared at the end, because it is used to indicate that other windows can
// use seamless rotation when applying rotation to display.
for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(
false /* replacing */);
final WindowToken token = state.mAssociatedTokens.get(i);
token.mFixedRotationTransformState = null;
token.notifyFixedRotationTransform(false /* enabled */);
}
}
private void cleanUpFixedRotationTransformState(boolean replacing) {
if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) {
// The state is not only used by self. Make sure to leave the influence by others.
mFixedRotationTransformState.mAssociatedTokens.remove(this);
mFixedRotationTransformState.mRotatedContainers.remove(this);
}
mFixedRotationTransformState = null;
notifyFixedRotationTransform(false /* enabled */);
}
/** Notifies application side to enable or disable the rotation adjustment of display info. */
private void notifyFixedRotationTransform(boolean enabled) {
FixedRotationAdjustments adjustments = null;
@@ -704,8 +717,9 @@ class WindowToken extends WindowContainer<WindowState> {
if (!isFixedRotationTransforming()) {
return null;
}
return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation,
mFixedRotationTransformState.mDisplayInfo.displayCutout);
final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
displayInfo.appHeight, displayInfo.displayCutout);
}
@Override

View File

@@ -1163,6 +1163,8 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.getDisplayRotation().setRotation(ROTATION_0);
mDisplayContent.computeScreenConfiguration(config);
mDisplayContent.onRequestedOverrideConfigurationChanged(config);
assertNotEquals(config90.windowConfiguration.getMaxBounds(),
config.windowConfiguration.getMaxBounds());
final ActivityRecord app = mAppWindow.mActivityRecord;
app.setVisible(false);
@@ -1218,8 +1220,9 @@ public class DisplayContentTests extends WindowTestsBase {
verify(t, never()).setPosition(any(), eq(0), eq(0));
// Launch another activity before the transition is finished.
final ActivityRecord app2 = new TaskBuilder(mSupervisor)
.setDisplay(mDisplayContent).setCreateActivity(true).build().getTopMostActivity();
final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
final ActivityRecord app2 = new ActivityBuilder(mAtm).setStack(task2)
.setUseProcess(app.app).build();
app2.setVisible(false);
mDisplayContent.mOpeningApps.add(app2);
app2.setRequestedOrientation(newOrientation);
@@ -1229,6 +1232,12 @@ public class DisplayContentTests extends WindowTestsBase {
assertTrue(app.hasFixedRotationTransform(app2));
assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration());
expectedProcConfig.windowConfiguration.setActivityType(
WindowConfiguration.ACTIVITY_TYPE_UNDEFINED);
assertEquals("The process should receive rotated configuration for compatibility",
expectedProcConfig, app2.app.getConfiguration());
// The fixed rotation transform can only be finished when all animation finished.
doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);

View File

@@ -266,6 +266,15 @@ public class WindowProcessControllerTests extends WindowTestsBase {
mWpc.onMergedOverrideConfigurationChanged(config);
assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType());
assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType());
final int globalSeq = 100;
mRootWindowContainer.getConfiguration().seq = globalSeq;
invertOrientation(mWpc.getConfiguration());
new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(mWpc).build();
assertTrue(mWpc.registeredForActivityConfigChanges());
assertEquals("Config seq of process should not be affected by activity",
mWpc.getConfiguration().seq, globalSeq);
}
private TestDisplayContent createTestDisplayContentInContainer() {