diff options
author | Wale Ogunwale <ogunwale@google.com> | 2015-01-23 16:05:07 -0800 |
---|---|---|
committer | Wale Ogunwale <ogunwale@google.com> | 2015-01-27 07:50:58 -0800 |
commit | 60454dbc4d815c90ff2713e224953d6547fc3ad5 (patch) | |
tree | 94e4368506d8c3438702f40e78ed29c78d1a43e8 | |
parent | 6c67474dd4f6eb0894121dac67e797073a02980a (diff) | |
download | frameworks_base-60454dbc4d815c90ff2713e224953d6547fc3ad5.zip frameworks_base-60454dbc4d815c90ff2713e224953d6547fc3ad5.tar.gz frameworks_base-60454dbc4d815c90ff2713e224953d6547fc3ad5.tar.bz2 |
Support activities in the same process having different resources.
Activities can be of various sizes in a multi-window environment.
This change allows them to have override configurations that allows
different resources to the loaded if needed.
Bug: 19002213
Change-Id: Ib2c7be0b427f5ce05e7a362bcdd496ddbc9164f0
15 files changed, 238 insertions, 141 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 23b05c4..a597d5e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4607,7 +4607,7 @@ public class Activity extends ContextThemeWrapper if (Looper.myLooper() != mMainThread.getLooper()) { throw new IllegalStateException("Must be called from main thread"); } - mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false); + mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false); } /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ea37b89..de3c95c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -293,6 +293,7 @@ public final class ActivityThread { boolean hideForNow; Configuration newConfig; Configuration createdConfig; + Configuration overrideConfig; ActivityClientRecord nextIdle; ProfilerInfo profilerInfo; @@ -615,12 +616,13 @@ public final class ActivityThread { // we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) + @Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, - PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, - ProfilerInfo profilerInfo) { + ActivityInfo info, Configuration curConfig, Configuration overrideConfig, + CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + int procState, Bundle state, PersistableBundle persistentState, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); @@ -644,16 +646,19 @@ public final class ActivityThread { r.profilerInfo = profilerInfo; + r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); } + @Override public final void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, boolean notResumed, Configuration config) { + int configChanges, boolean notResumed, Configuration config, + Configuration overrideConfig) { requestRelaunchActivity(token, pendingResults, pendingNewIntents, - configChanges, notResumed, config, true); + configChanges, notResumed, config, overrideConfig, true); } public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) { @@ -887,6 +892,7 @@ public final class ActivityThread { sendMessage(H.LOW_MEMORY, null); } + @Override public void scheduleActivityConfigurationChanged(IBinder token) { sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token); } @@ -1668,7 +1674,7 @@ public final class ActivityThread { String[] libDirs, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, - displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); + displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo()); } final Handler getHandler() { @@ -2353,7 +2359,8 @@ public final class ActivityThread { private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { - ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token); + ContextImpl appContext = + ContextImpl.createActivityContext(this, r.packageInfo, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; @@ -3561,7 +3568,7 @@ public final class ActivityThread { // request all activities to relaunch for the changes to take place for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { - requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, false); + requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false); } } } @@ -3805,7 +3812,7 @@ public final class ActivityThread { public final void requestRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config, - boolean fromServer) { + Configuration overrideConfig, boolean fromServer) { ActivityClientRecord target = null; synchronized (mResourcesManager) { @@ -3854,6 +3861,9 @@ public final class ActivityThread { if (config != null) { target.createdConfig = config; } + if (overrideConfig != null) { + target.overrideConfig = overrideConfig; + } target.pendingConfigChanges |= configChanges; } } @@ -3966,6 +3976,7 @@ public final class ActivityThread { } } r.startsNotResumed = tmp.startsNotResumed; + r.overrideConfig = tmp.overrideConfig; handleLaunchActivity(r, currentIntent); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index eb3ddb2..349b66d 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -140,6 +140,10 @@ public abstract class ApplicationThreadNative extends Binder int ident = data.readInt(); ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data); Configuration curConfig = Configuration.CREATOR.createFromParcel(data); + Configuration overrideConfig = null; + if (data.readInt() != 0) { + overrideConfig = Configuration.CREATOR.createFromParcel(data); + } CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); String referrer = data.readString(); IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface( @@ -153,8 +157,8 @@ public abstract class ApplicationThreadNative extends Binder boolean isForward = data.readInt() != 0; ProfilerInfo profilerInfo = data.readInt() != 0 ? ProfilerInfo.CREATOR.createFromParcel(data) : null; - scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, referrer, - voiceInteractor, procState, state, persistentState, ri, pi, + scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo, + referrer, voiceInteractor, procState, state, persistentState, ri, pi, notResumed, isForward, profilerInfo); return true; } @@ -167,14 +171,15 @@ public abstract class ApplicationThreadNative extends Binder List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); int configChanges = data.readInt(); boolean notResumed = data.readInt() != 0; - Configuration config = null; + Configuration config = Configuration.CREATOR.createFromParcel(data); + Configuration overrideConfig = null; if (data.readInt() != 0) { - config = Configuration.CREATOR.createFromParcel(data); + overrideConfig = Configuration.CREATOR.createFromParcel(data); } - scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config); + scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig); return true; } - + case SCHEDULE_NEW_INTENT_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); @@ -775,11 +780,11 @@ class ApplicationThreadProxy implements IApplicationThread { } public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, - PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, - ProfilerInfo profilerInfo) throws RemoteException { + ActivityInfo info, Configuration curConfig, Configuration overrideConfig, + CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + int procState, Bundle state, PersistableBundle persistentState, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); intent.writeToParcel(data, 0); @@ -787,6 +792,12 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(ident); info.writeToParcel(data, 0); curConfig.writeToParcel(data, 0); + if (overrideConfig != null) { + data.writeInt(1); + overrideConfig.writeToParcel(data, 0); + } else { + data.writeInt(0); + } compatInfo.writeToParcel(data, 0); data.writeString(referrer); data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null); @@ -810,8 +821,8 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, - int configChanges, boolean notResumed, Configuration config) - throws RemoteException { + int configChanges, boolean notResumed, Configuration config, + Configuration overrideConfig) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); @@ -819,9 +830,10 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeTypedList(pendingNewIntents); data.writeInt(configChanges); data.writeInt(notResumed ? 1 : 0); - if (config != null) { + config.writeToParcel(data, 0); + if (overrideConfig != null) { data.writeInt(1); - config.writeToParcel(data, 0); + overrideConfig.writeToParcel(data, 0); } else { data.writeInt(0); } @@ -1112,8 +1124,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public final void scheduleActivityConfigurationChanged( - IBinder token) throws RemoteException { + public final void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index e9df8c6..39caf0b 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2264,11 +2264,10 @@ class ContextImpl extends Context { } static ContextImpl createActivityContext(ActivityThread mainThread, - LoadedApk packageInfo, IBinder activityToken) { + LoadedApk packageInfo, Configuration overrideConfiguration) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); - if (activityToken == null) throw new IllegalArgumentException("activityInfo"); - return new ContextImpl(null, mainThread, - packageInfo, activityToken, null, false, null, null); + return new ContextImpl(null, mainThread, packageInfo, null, null, false, null, + overrideConfiguration); } private ContextImpl(ContextImpl container, ActivityThread mainThread, @@ -2303,15 +2302,14 @@ class ContextImpl extends Context { Resources resources = packageInfo.getResources(mainThread); if (resources != null) { - if (activityToken != null - || displayId != Display.DEFAULT_DISPLAY + if (displayId != Display.DEFAULT_DISPLAY || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, - overrideConfiguration, compatInfo, activityToken); + overrideConfiguration, compatInfo); } } mResources = resources; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 8bf8cd7..f2c912e 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -33,7 +33,6 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; -import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -59,14 +58,14 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, - PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, - ProfilerInfo profilerInfo) throws RemoteException; + ActivityInfo info, Configuration curConfig, Configuration overrideConfig, + CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, + int procState, Bundle state, PersistableBundle persistentState, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, + boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, - List<ReferrerIntent> pendingNewIntents, int configChanges, - boolean notResumed, Configuration config) throws RemoteException; + List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, + Configuration config, Configuration overrideConfig) throws RemoteException; void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException; void scheduleDestroyActivity(IBinder token, boolean finished, int configChanges) throws RemoteException; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 1691d8e..fac40b2 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -25,7 +25,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.ResourcesKey; import android.hardware.display.DisplayManagerGlobal; -import android.os.IBinder; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Slog; @@ -38,8 +37,7 @@ import java.util.Locale; /** @hide */ public class ResourcesManager { static final String TAG = "ResourcesManager"; - static final boolean DEBUG_CACHE = false; - static final boolean DEBUG_STATS = true; + private static final boolean DEBUG = false; private static ResourcesManager sResourcesManager; final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources @@ -51,7 +49,6 @@ public class ResourcesManager { CompatibilityInfo mResCompatibilityInfo; Configuration mResConfiguration; - final Configuration mTmpConfig = new Configuration(); public static ResourcesManager getInstance() { synchronized (ResourcesManager.class) { @@ -144,30 +141,32 @@ public class ResourcesManager { * Creates the top level Resources for applications with the given compatibility info. * * @param resDir the resource directory. + * @param splitResDirs split resource directories. * @param overlayDirs the resource overlay directories. * @param libDirs the shared library resource dirs this app references. - * @param compatInfo the compability info. Must not be null. - * @param token the application token for determining stack bounds. + * @param displayId display Id. + * @param overrideConfiguration override configurations. + * @param compatInfo the compatibility info. Must not be null. */ public Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, - Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { + Configuration overrideConfiguration, CompatibilityInfo compatInfo) { final float scale = compatInfo.applicationScale; - ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token); + Configuration overrideConfigCopy = (overrideConfiguration != null) + ? new Configuration(overrideConfiguration) : null; + ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale); Resources r; synchronized (this) { // Resources is app scale dependent. - if (false) { - Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); - } + if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale); + WeakReference<Resources> wr = mActiveResources.get(key); r = wr != null ? wr.get() : null; //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate()); if (r != null && r.getAssets().isUpToDate()) { - if (false) { - Slog.w(TAG, "Returning cached resources " + r + " " + resDir - + ": appScale=" + r.getCompatibilityInfo().applicationScale); - } + if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir + + ": appScale=" + r.getCompatibilityInfo().applicationScale + + " key=" + key + " overrideConfig=" + overrideConfiguration); return r; } } @@ -213,7 +212,7 @@ public class ResourcesManager { //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); DisplayMetrics dm = getDisplayMetricsLocked(displayId); Configuration config; - boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); final boolean hasOverrideConfig = key.hasOverrideConfiguration(); if (!isDefaultDisplay || hasOverrideConfig) { config = new Configuration(getConfiguration()); @@ -222,16 +221,14 @@ public class ResourcesManager { } if (hasOverrideConfig) { config.updateFrom(key.mOverrideConfiguration); + if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration); } } else { config = getConfiguration(); } - r = new Resources(assets, dm, config, compatInfo, token); - if (false) { - Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " - + r.getConfiguration() + " appScale=" - + r.getCompatibilityInfo().applicationScale); - } + r = new Resources(assets, dm, config, compatInfo); + if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale); synchronized (this) { WeakReference<Resources> wr = mActiveResources.get(key); @@ -244,7 +241,8 @@ public class ResourcesManager { } // XXX need to remove entries when weak references go away - mActiveResources.put(key, new WeakReference<Resources>(r)); + mActiveResources.put(key, new WeakReference<>(r)); + if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size()); return r; } } @@ -255,7 +253,7 @@ public class ResourcesManager { mResConfiguration = new Configuration(); } if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" + if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" + mResConfiguration.seq + ", newSeq=" + config.seq); return false; } @@ -287,7 +285,7 @@ public class ResourcesManager { ResourcesKey key = mActiveResources.keyAt(i); Resources r = mActiveResources.valueAt(i).get(); if (r != null) { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " + if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " + r + " config to: " + config); int displayId = key.mDisplayId; boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index c931c01..6fb7299 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -141,9 +141,6 @@ public class Resources { private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; - @SuppressWarnings("unused") - private WeakReference<IBinder> mToken; - static { sPreloadedDrawables = new LongSparseArray[2]; sPreloadedDrawables[0] = new LongSparseArray<ConstantState>(); @@ -224,46 +221,44 @@ public class Resources { /** * Create a new Resources object on top of an existing set of assets in an * AssetManager. - * - * @param assets Previously created AssetManager. - * @param metrics Current display metrics to consider when + * + * @param assets Previously created AssetManager. + * @param metrics Current display metrics to consider when * selecting/computing resource values. - * @param config Desired device configuration to consider when + * @param config Desired device configuration to consider when * selecting/computing resource values (optional). */ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { - this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO); } /** * Creates a new Resources object with CompatibilityInfo. - * - * @param assets Previously created AssetManager. - * @param metrics Current display metrics to consider when + * + * @param assets Previously created AssetManager. + * @param metrics Current display metrics to consider when * selecting/computing resource values. - * @param config Desired device configuration to consider when + * @param config Desired device configuration to consider when * selecting/computing resource values (optional). * @param compatInfo this resource's compatibility info. Must not be null. - * @param token The Activity token for determining stack affiliation. Usually null. * @hide */ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config, - CompatibilityInfo compatInfo, IBinder token) { + CompatibilityInfo compatInfo) { mAssets = assets; mMetrics.setToDefaults(); if (compatInfo != null) { mCompatibilityInfo = compatInfo; } - mToken = new WeakReference<IBinder>(token); updateConfiguration(config, metrics); assets.ensureStringBlocks(); } /** * Return a global shared Resources object that provides access to only - * system resources (no application resources), and is not configured for - * the current screen (can not use dimension units, does not change based - * on orientation, etc). + * system resources (no application resources), and is not configured for + * the current screen (can not use dimension units, does not change based + * on orientation, etc). */ public static Resources getSystem() { synchronized (sSync) { diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index 4ae3825..9548d49 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -16,27 +16,23 @@ package android.content.res; -import android.os.IBinder; +import java.util.Objects; /** @hide */ public final class ResourcesKey { - final String mResDir; - final float mScale; + private final String mResDir; + private final float mScale; private final int mHash; - private final IBinder mToken; public final int mDisplayId; - public final Configuration mOverrideConfiguration = new Configuration(); + public final Configuration mOverrideConfiguration; public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, - float scale, IBinder token) { + float scale) { mResDir = resDir; mDisplayId = displayId; - if (overrideConfiguration != null) { - mOverrideConfiguration.setTo(overrideConfiguration); - } + mOverrideConfiguration = overrideConfiguration; mScale = scale; - mToken = token; int hash = 17; hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode()); @@ -48,7 +44,8 @@ public final class ResourcesKey { } public boolean hasOverrideConfiguration() { - return !Configuration.EMPTY.equals(mOverrideConfiguration); + return mOverrideConfiguration != null + && !Configuration.EMPTY.equals(mOverrideConfiguration); } @Override @@ -63,17 +60,9 @@ public final class ResourcesKey { } ResourcesKey peer = (ResourcesKey) obj; - if ((mResDir == null) && (peer.mResDir != null)) { - return false; - } - if ((mResDir != null) && (peer.mResDir == null)) { + if (!Objects.equals(mResDir, peer.mResDir)) { return false; } - if ((mResDir != null) && (peer.mResDir != null)) { - if (!mResDir.equals(peer.mResDir)) { - return false; - } - } if (mDisplayId != peer.mDisplayId) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2935bc0..2b65c18 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8162,12 +8162,14 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void resizeStack(int stackBoxId, Rect bounds) { + public void resizeStack(int stackId, Rect bounds) { enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, "resizeStackBox()"); long ident = Binder.clearCallingIdentity(); try { - mWindowManager.resizeStack(stackBoxId, bounds); + synchronized (this) { + mStackSupervisor.resizeStackLocked(stackId, bounds); + } } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index af6a314..8569ea4 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -127,6 +127,10 @@ final class ActivityRecord { long pauseTime; // last time we started pausing the activity long launchTickTime; // base time for launch tick messages Configuration configuration; // configuration activity was last running in + // Overridden configuration by the activity stack + // WARNING: Reference points to {@link ActivityStack#mOverrideConfig}, so its internal state + // should never be altered directly. + Configuration stackConfigOverride; CompatibilityInfo compat;// last used compatibility mode ActivityRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. @@ -204,6 +208,7 @@ final class ActivityRecord { pw.print(" icon=0x"); pw.print(Integer.toHexString(icon)); pw.print(" theme=0x"); pw.println(Integer.toHexString(theme)); pw.print(prefix); pw.print("config="); pw.println(configuration); + pw.print(prefix); pw.print("stackConfigOverride="); pw.println(stackConfigOverride); if (resultTo != null || resultWho != null) { pw.print(prefix); pw.print("resultTo="); pw.print(resultTo); pw.print(" resultWho="); pw.print(resultWho); @@ -394,6 +399,8 @@ final class ActivityRecord { resolvedType = _resolvedType; componentSpecified = _componentSpecified; configuration = _configuration; + stackConfigOverride = (container != null) + ? container.mStack.mOverrideConfig : Configuration.EMPTY; resultTo = _resultTo; resultWho = _resultWho; requestCode = _reqCode; @@ -1066,10 +1073,7 @@ final class ActivityRecord { static ActivityRecord isInStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.forToken(token); - if (r != null) { - return r.task.stack.isInStackLocked(token); - } - return null; + return (r != null) ? r.task.stack.isInStackLocked(r) : null; } static ActivityStack getStackLocked(IBinder token) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 5d21de7..7c1d54c 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -235,6 +235,8 @@ final class ActivityStack { /** Run all ActivityStacks through this */ final ActivityStackSupervisor mStackSupervisor; + Configuration mOverrideConfig; + static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1; static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2; static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3; @@ -348,6 +350,7 @@ final class ActivityStack { mStackId = activityContainer.mStackId; mCurrentUser = mService.mCurrentUserId; mRecentTasks = recentTasks; + mOverrideConfig = Configuration.EMPTY; } /** @@ -450,13 +453,18 @@ final class ActivityStack { ActivityRecord isInStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.forToken(token); - if (r != null) { - final TaskRecord task = r.task; - if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { - if (task.stack != this) Slog.w(TAG, + return isInStackLocked(r); + } + + ActivityRecord isInStackLocked(ActivityRecord r) { + if (r == null) { + return null; + } + final TaskRecord task = r.task; + if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { + if (task.stack != this) Slog.w(TAG, "Illegal state! task does not point to stack it is in."); - return r; - } + return r; } return null; } @@ -3264,7 +3272,7 @@ final class ActivityStack { } if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r); - if (isInStackLocked(token) != null) { + if (isInStackLocked(r) != null) { if (r.state == ActivityState.DESTROYING) { cleanUpActivityLocked(r, true, false); removeActivityFromHistoryLocked(r); @@ -3643,7 +3651,9 @@ final class ActivityStack { // Short circuit: if the two configurations are the exact same // object (the common case), then there is nothing to do. Configuration newConfig = mService.mConfiguration; - if (r.configuration == newConfig && !r.forceNewConfig) { + if (r.configuration == newConfig + && r.stackConfigOverride == mOverrideConfig + && !r.forceNewConfig) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration unchanged in " + r); return true; @@ -3659,14 +3669,22 @@ final class ActivityStack { // Okay we now are going to make this activity have the new config. // But then we need to figure out how it needs to deal with that. - Configuration oldConfig = r.configuration; + final Configuration oldConfig = r.configuration; + final Configuration oldStackOverride = r.stackConfigOverride; r.configuration = newConfig; + r.stackConfigOverride = mOverrideConfig; // Determine what has changed. May be nothing, if this is a config // that has come back from the app after going idle. In that case // we just want to leave the official config object now in the // activity and do nothing else. - final int changes = oldConfig.diff(newConfig); + int stackChanges = oldStackOverride.diff(mOverrideConfig); + if (stackChanges == 0 && !oldStackOverride.equals(mOverrideConfig)) { + // Assume size change if diff didn't report any changes, + // but configurations are not equal. + stackChanges = ActivityInfo.CONFIG_SCREEN_SIZE; + } + final int changes = oldConfig.diff(newConfig) | stackChanges; if (changes == 0 && !r.forceNewConfig) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration no differences in " + r); @@ -3770,8 +3788,9 @@ final class ActivityStack { (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ") + r); r.forceNewConfig = false; - r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, - changes, !andResume, new Configuration(mService.mConfiguration)); + r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes, + !andResume, new Configuration(mService.mConfiguration), + new Configuration(mOverrideConfig)); // Note: don't need to call pauseIfSleepingLocked() here, because // the caller will only pass in 'andResume' if this activity is // currently resumed, which implies we aren't sleeping. @@ -4132,4 +4151,10 @@ final class ActivityStack { return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this)) + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}"; } + + boolean updateOverrideConfiguration(Configuration newConfig) { + Configuration oldConfig = mOverrideConfig; + mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig; + return !mOverrideConfig.equals(oldConfig); + } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4cf8f00..b1bb02a 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -63,6 +63,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.graphics.Point; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerGlobal; @@ -874,8 +875,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } else { stack = container.mStack; } - stack.mConfigWillChange = config != null - && mService.mConfiguration.diff(config) != 0; + stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0; if (DEBUG_CONFIGURATION) Slog.v(TAG, "Starting activity when config will change = " + stack.mConfigWillChange); @@ -1181,9 +1181,9 @@ public final class ActivityStackSupervisor implements DisplayListener { app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP); app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), - r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState, - r.icicle, r.persistentState, results, newIntents, !andResume, - mService.isNextTransitionForward(), profilerInfo); + new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, + r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, + newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { // This may be a heavy-weight process! Note that the package @@ -2595,6 +2595,27 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + void resizeStackLocked(int stackId, Rect bounds) { + final ActivityStack stack = getStack(stackId); + if (stack == null) { + Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); + return; + } + final Configuration overrideConfig = mWindowManager.resizeStack(stackId, bounds); + if (stack.updateOverrideConfiguration(overrideConfig)) { + final ActivityRecord r = stack.topRunningActivityLocked(null); + if (r != null) { + final boolean updated = stack.ensureActivityConfigurationLocked(r, 0); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + ensureActivitiesVisibleLocked(r, 0); + if (!updated) { + resumeTopActivitiesLocked(stack, null, null); + } + } + } + } + ActivityStack createStackOnDisplay(int stackId, int displayId) { ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); if (activityDisplay == null) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index d6741b3..d6927e1 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -19,8 +19,10 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerService.TAG; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; +import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; import android.util.TypedValue; @@ -78,9 +80,14 @@ public class TaskStack { /** Detach this stack from its display when animation completes. */ boolean mDeferDetach; + // Contains configurations settings that are different from the global configuration due to + // stack specific operations. E.g. {@link #setBounds}. + Configuration mOverrideConfig; + TaskStack(WindowManagerService service, int stackId) { mService = service; mStackId = stackId; + mOverrideConfig = Configuration.EMPTY; // TODO: remove bounds from log, they are always 0. EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top, mBounds.right, mBounds.bottom); @@ -129,7 +136,7 @@ public class TaskStack { mDimLayer.setBounds(bounds); mAnimationBackgroundSurface.setBounds(bounds); mBounds.set(bounds); - + updateOverrideConfiguration(); return true; } @@ -137,6 +144,27 @@ public class TaskStack { out.set(mBounds); } + void updateOverrideConfiguration() { + final Configuration serviceConfig = mService.mCurConfiguration; + if (mFullscreen) { + mOverrideConfig = Configuration.EMPTY; + return; + } + + if (mOverrideConfig == Configuration.EMPTY) { + mOverrideConfig = new Configuration(); + } + + // TODO(multidisplay): Update Dp to that of display stack is on. + final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; + mOverrideConfig.screenWidthDp = + Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp); + mOverrideConfig.screenHeightDp = + Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp); + mOverrideConfig.smallestScreenWidthDp = + Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp); + } + void updateDisplayInfo() { if (mFullscreen && mDisplayContent != null) { mDisplayContent.getLogicalDisplayRect(mTmpRect); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2c05e93..46db132 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5128,7 +5128,12 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void resizeStack(int stackId, Rect bounds) { + /** + * Re-sizes the specified stack and its containing windows. + * Returns a {@link Configuration} object that contains configurations settings + * that should be overridden due to the operation. + */ + public Configuration resizeStack(int stackId, Rect bounds) { synchronized (mWindowMap) { final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { @@ -5140,6 +5145,7 @@ public class WindowManagerService extends IWindowManager.Stub stack.getDisplayContent().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } + return new Configuration(stack.mOverrideConfig); } } @@ -7068,7 +7074,7 @@ public class WindowManagerService extends IWindowManager.Stub return sw; } - boolean computeScreenConfigurationLocked(Configuration config) { + private boolean computeScreenConfigurationLocked(Configuration config) { if (!mDisplayReady) { return false; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 978f5c3..a22ba33 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -130,7 +130,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { int mLayoutSeq = -1; - Configuration mConfiguration = null; + private Configuration mConfiguration = Configuration.EMPTY; + private Configuration mOverrideConfig = Configuration.EMPTY; // Sticky answer to isConfigChanged(), remains true until new Configuration is assigned. // Used only on {@link #TYPE_KEYGUARD}. private boolean mConfigHasChanged; @@ -1076,9 +1077,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean isConfigChanged() { - boolean configChanged = mConfiguration != mService.mCurConfiguration - && (mConfiguration == null - || (mConfiguration.diff(mService.mCurConfiguration) != 0)); + final TaskStack stack = getStack(); + final Configuration overrideConfig = + (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY; + final Configuration serviceConfig = mService.mCurConfiguration; + boolean configChanged = + (mConfiguration != serviceConfig && mConfiguration.diff(serviceConfig) != 0) + || (mOverrideConfig != overrideConfig && !mOverrideConfig.equals(overrideConfig)); if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { // Retain configuration changed status until resetConfiguration called. @@ -1107,8 +1112,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } - void setConfiguration(final Configuration newConfig) { + private void setConfiguration( + final Configuration newConfig, final Configuration newOverrideConfig) { mConfiguration = newConfig; + mOverrideConfig = newOverrideConfig; mConfigHasChanged = false; } @@ -1384,12 +1391,15 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this + ": " + mCompatFrame); boolean configChanged = isConfigChanged(); + final TaskStack stack = getStack(); + final Configuration overrideConfig = + (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY; if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) { Slog.i(TAG, "Sending new config to window " + this + ": " - + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH - + " / " + mService.mCurConfiguration); + + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH + " / config=" + + mService.mCurConfiguration + " overrideConfig=" + overrideConfig); } - setConfiguration(mService.mCurConfiguration); + setConfiguration(mService.mCurConfiguration, overrideConfig); if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); |