Merge "Add window layout affinity." into rvc-dev am: 7fcb7d59c4 am: 807b8e42df am: 3d0ba25d61 am: a24ecf75a0

Change-Id: I603abe4ab9a8008e3b169d89a3b5933d562a349b
This commit is contained in:
Automerger Merge Worker
2020-03-13 17:07:59 +00:00
8 changed files with 328 additions and 44 deletions

View File

@@ -1222,13 +1222,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
dest.writeInt(lockTaskLaunchMode);
if (windowLayout != null) {
dest.writeInt(1);
dest.writeInt(windowLayout.width);
dest.writeFloat(windowLayout.widthFraction);
dest.writeInt(windowLayout.height);
dest.writeFloat(windowLayout.heightFraction);
dest.writeInt(windowLayout.gravity);
dest.writeInt(windowLayout.minWidth);
dest.writeInt(windowLayout.minHeight);
windowLayout.writeToParcel(dest);
} else {
dest.writeInt(0);
}
@@ -1372,8 +1366,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @attr ref android.R.styleable#AndroidManifestLayout_minHeight
*/
public static final class WindowLayout {
public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity,
int minWidth, int minHeight) {
public WindowLayout(int width, float widthFraction, int height, float heightFraction,
int gravity, int minWidth, int minHeight) {
this.width = width;
this.widthFraction = widthFraction;
this.height = height;
@@ -1392,6 +1386,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
gravity = source.readInt();
minWidth = source.readInt();
minHeight = source.readInt();
windowLayoutAffinity = source.readString();
}
/**
@@ -1457,6 +1452,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
public final int minHeight;
/**
* Affinity of window layout parameters. Activities with the same UID and window layout
* affinity will share the same window dimension record.
* @hide
*/
public String windowLayoutAffinity;
/**
* Returns if this {@link WindowLayout} has specified bounds.
* @hide
@@ -1464,5 +1466,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public boolean hasSpecifiedSize() {
return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0;
}
/** @hide */
public void writeToParcel(Parcel dest) {
dest.writeInt(width);
dest.writeFloat(widthFraction);
dest.writeInt(height);
dest.writeFloat(heightFraction);
dest.writeInt(gravity);
dest.writeInt(minWidth);
dest.writeInt(minHeight);
dest.writeString(windowLayoutAffinity);
}
}
}

View File

@@ -55,7 +55,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
@@ -209,6 +208,8 @@ public class PackageParser {
public static final String TAG_USES_SPLIT = "uses-split";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
/**
* Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
@@ -4560,6 +4561,8 @@ public class PackageParser {
}
}
resolveWindowLayout(a);
if (!setExported) {
a.info.exported = a.intents.size() > 0;
}
@@ -4726,6 +4729,35 @@ public class PackageParser {
height, heightFraction, gravity, minWidth, minHeight);
}
/**
* Resolves values in {@link ActivityInfo.WindowLayout}.
*
* <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
* Android R and some variants of pre-R.
*/
private void resolveWindowLayout(Activity activity) {
// There isn't a metadata for us to fall back. Whatever is in layout is correct.
if (activity.metaData == null
|| !activity.metaData.containsKey(METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
return;
}
final ActivityInfo aInfo = activity.info;
// Layout already specifies a value. We should just use that one.
if (aInfo.windowLayout != null && aInfo.windowLayout.windowLayoutAffinity != null) {
return;
}
String windowLayoutAffinity = activity.metaData.getString(
METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
if (aInfo.windowLayout == null) {
aInfo.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */,
-1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
Gravity.NO_GRAVITY, -1 /* minWidth */, -1 /* minHeight */);
}
aInfo.windowLayout.windowLayoutAffinity = windowLayoutAffinity;
}
private Activity parseActivityAlias(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
CachedComponentArgs cachedArgs)

View File

@@ -285,14 +285,8 @@ public class ParsedActivity extends ParsedMainComponent {
dest.writeBundle(this.metaData);
if (windowLayout != null) {
dest.writeBoolean(true);
dest.writeInt(windowLayout.width);
dest.writeFloat(windowLayout.widthFraction);
dest.writeInt(windowLayout.height);
dest.writeFloat(windowLayout.heightFraction);
dest.writeInt(windowLayout.gravity);
dest.writeInt(windowLayout.minWidth);
dest.writeInt(windowLayout.minHeight);
dest.writeInt(1);
windowLayout.writeToParcel(dest);
} else {
dest.writeBoolean(false);
}

View File

@@ -17,16 +17,17 @@
package android.content.pm.parsing.component;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.parsing.component.ComponentParseUtils.flag;
import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -42,9 +43,6 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -379,6 +377,12 @@ public class ParsedActivityUtils {
}
}
ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
if (layoutResult.isError()) {
return input.error(layoutResult);
}
activity.windowLayout = layoutResult.getResult();
if (!setExported) {
activity.exported = activity.getIntents().size() > 0;
}
@@ -481,4 +485,35 @@ public class ParsedActivityUtils {
sw.recycle();
}
}
/**
* Resolves values in {@link ActivityInfo.WindowLayout}.
*
* <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
* Android R and some variants of pre-R.
*/
private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
ParsedActivity activity, ParseInput input) {
// There isn't a metadata for us to fall back. Whatever is in layout is correct.
if (activity.metaData == null || !activity.metaData.containsKey(
PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
return input.success(activity.windowLayout);
}
// Layout already specifies a value. We should just use that one.
if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
return input.success(activity.windowLayout);
}
String windowLayoutAffinity = activity.metaData.getString(
PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
ActivityInfo.WindowLayout layout = activity.windowLayout;
if (layout == null) {
layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
-1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
-1 /* minWidth */, -1 /* minHeight */);
}
layout.windowLayoutAffinity = windowLayoutAffinity;
return input.success(layout);
}
}

View File

@@ -1589,6 +1589,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
info.taskAffinity = uid + ":" + info.taskAffinity;
}
taskAffinity = info.taskAffinity;
if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
&& !info.windowLayout.windowLayoutAffinity.startsWith(uid)) {
info.windowLayout.windowLayoutAffinity =
uid + ":" + info.windowLayout.windowLayoutAffinity;
}
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;

View File

@@ -16,7 +16,9 @@
package com.android.server.wm;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.Environment;
@@ -87,9 +89,17 @@ class LaunchParamsPersister {
* launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata
* that are stable across reboots.
*/
private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mMap =
private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mLaunchParamsMap =
new SparseArray<>();
/**
* A map from {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} to
* activity's component name for reverse queries from window layout affinities to activities.
* Used to decide if we should use another activity's record with the same affinity.
*/
private final ArrayMap<String, ArraySet<ComponentName>> mWindowLayoutAffinityMap =
new ArrayMap<>();
LaunchParamsPersister(PersisterQueue persisterQueue, ActivityStackSupervisor supervisor) {
this(persisterQueue, supervisor, Environment::getDataSystemCeDirectory);
}
@@ -112,7 +122,7 @@ class LaunchParamsPersister {
}
void onCleanupUser(int userId) {
mMap.remove(userId);
mLaunchParamsMap.remove(userId);
}
private void loadLaunchParams(int userId) {
@@ -128,7 +138,7 @@ class LaunchParamsPersister {
final File[] paramsFiles = launchParamsFolder.listFiles();
final ArrayMap<ComponentName, PersistableLaunchParams> map =
new ArrayMap<>(paramsFiles.length);
mMap.put(userId, map);
mLaunchParamsMap.put(userId, map);
for (File paramsFile : paramsFiles) {
if (!paramsFile.isFile()) {
@@ -179,10 +189,12 @@ class LaunchParamsPersister {
continue;
}
params.restoreFromXml(parser);
params.restore(paramsFile, parser);
}
map.put(name, params);
addComponentNameToLaunchParamAffinityMapIfNotNull(
name, params.mWindowLayoutAffinity);
} catch (Exception e) {
Slog.w(TAG, "Failed to restore launch params for " + name, e);
filesToDelete.add(paramsFile);
@@ -204,19 +216,17 @@ class LaunchParamsPersister {
final ComponentName name = task.realActivity;
final int userId = task.mUserId;
PersistableLaunchParams params;
ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
if (map == null) {
map = new ArrayMap<>();
mMap.put(userId, map);
mLaunchParamsMap.put(userId, map);
}
params = map.get(name);
if (params == null) {
params = new PersistableLaunchParams();
map.put(name, params);
}
params = map.computeIfAbsent(name, componentName -> new PersistableLaunchParams());
final boolean changed = saveTaskToLaunchParam(task, display, params);
addComponentNameToLaunchParamAffinityMapIfNotNull(name, params.mWindowLayoutAffinity);
if (changed) {
mPersisterQueue.updateLastOrAddItem(
new LaunchParamsWriteQueueItem(userId, name, params),
@@ -243,19 +253,63 @@ class LaunchParamsPersister {
params.mBounds.setEmpty();
}
String launchParamAffinity = task.mWindowLayoutAffinity;
changed |= Objects.equals(launchParamAffinity, params.mWindowLayoutAffinity);
params.mWindowLayoutAffinity = launchParamAffinity;
if (changed) {
params.mTimestamp = System.currentTimeMillis();
}
return changed;
}
private void addComponentNameToLaunchParamAffinityMapIfNotNull(
ComponentName name, String launchParamAffinity) {
if (launchParamAffinity == null) {
return;
}
mWindowLayoutAffinityMap.computeIfAbsent(launchParamAffinity, affinity -> new ArraySet<>())
.add(name);
}
void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) {
final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
final int userId = task != null ? task.mUserId : activity.mUserId;
final String windowLayoutAffinity;
if (task != null) {
windowLayoutAffinity = task.mWindowLayoutAffinity;
} else {
ActivityInfo.WindowLayout layout = activity.info.windowLayout;
windowLayoutAffinity = layout == null ? null : layout.windowLayoutAffinity;
}
outParams.reset();
Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
Map<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
if (map == null) {
return;
}
final PersistableLaunchParams persistableParams = map.get(name);
// First use its own record as a reference.
PersistableLaunchParams persistableParams = map.get(name);
// Next we'll compare these params against all existing params with the same affinity and
// use the newest one.
if (windowLayoutAffinity != null
&& mWindowLayoutAffinityMap.get(windowLayoutAffinity) != null) {
ArraySet<ComponentName> candidates = mWindowLayoutAffinityMap.get(windowLayoutAffinity);
for (int i = 0; i < candidates.size(); ++i) {
ComponentName candidate = candidates.valueAt(i);
final PersistableLaunchParams candidateParams = map.get(candidate);
if (candidateParams == null) {
continue;
}
if (persistableParams == null
|| candidateParams.mTimestamp > persistableParams.mTimestamp) {
persistableParams = candidateParams;
}
}
}
if (persistableParams == null) {
return;
@@ -272,10 +326,10 @@ class LaunchParamsPersister {
void removeRecordForPackage(String packageName) {
final List<File> fileToDelete = new ArrayList<>();
for (int i = 0; i < mMap.size(); ++i) {
int userId = mMap.keyAt(i);
for (int i = 0; i < mLaunchParamsMap.size(); ++i) {
int userId = mLaunchParamsMap.keyAt(i);
final File launchParamsFolder = getLaunchParamFolder(userId);
ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.valueAt(i);
ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.valueAt(i);
for (int j = map.size() - 1; j >= 0; --j) {
final ComponentName name = map.keyAt(j);
if (name.getPackageName().equals(packageName)) {
@@ -409,6 +463,7 @@ class LaunchParamsPersister {
private static final String ATTR_WINDOWING_MODE = "windowing_mode";
private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id";
private static final String ATTR_BOUNDS = "bounds";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
/** The bounds within the parent container. */
final Rect mBounds = new Rect();
@@ -419,14 +474,29 @@ class LaunchParamsPersister {
/** The windowing mode to be in. */
int mWindowingMode;
/**
* Last {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} of the
* window.
*/
@Nullable String mWindowLayoutAffinity;
/**
* Timestamp from {@link System#currentTimeMillis()} when this record is captured, or last
* modified time when the record is restored from storage.
*/
long mTimestamp;
void saveToXml(XmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId);
serializer.attribute(null, ATTR_WINDOWING_MODE,
Integer.toString(mWindowingMode));
serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
if (mWindowLayoutAffinity != null) {
serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
}
}
void restoreFromXml(XmlPullParser parser) {
void restore(File xmlFile, XmlPullParser parser) {
for (int i = 0; i < parser.getAttributeCount(); ++i) {
final String attrValue = parser.getAttributeValue(i);
switch (parser.getAttributeName(i)) {
@@ -443,16 +513,28 @@ class LaunchParamsPersister {
}
break;
}
case ATTR_WINDOW_LAYOUT_AFFINITY:
mWindowLayoutAffinity = attrValue;
break;
}
}
// The modified time could be a few seconds later than the timestamp when the record is
// captured, which is a good enough estimate to the capture time after a reboot or a
// user switch.
mTimestamp = xmlFile.lastModified();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("PersistableLaunchParams{");
builder.append("windowingMode=" + mWindowingMode);
builder.append(" windowingMode=" + mWindowingMode);
builder.append(" displayUniqueId=" + mDisplayUniqueId);
builder.append(" bounds=" + mBounds);
if (mWindowLayoutAffinity != null) {
builder.append(" launchParamsAffinity=" + mWindowLayoutAffinity);
}
builder.append(" timestamp=" + mTimestamp);
builder.append(" }");
return builder.toString();
}

View File

@@ -199,6 +199,7 @@ class Task extends WindowContainer<WindowContainer> {
private static final String ATTR_MIN_WIDTH = "min_width";
private static final String ATTR_MIN_HEIGHT = "min_height";
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
// Current version of the task record we persist. Used to check if we need to run any upgrade
// code.
@@ -233,6 +234,8 @@ class Task extends WindowContainer<WindowContainer> {
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving
// launch params of this task.
IVoiceInteractionSession voiceSession; // Voice interaction session driving task
IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task. Note that this value can
@@ -1001,6 +1004,8 @@ class Task extends WindowContainer<WindowContainer> {
origActivity = new ComponentName(info.packageName, info.name);
}
}
mWindowLayoutAffinity =
info.windowLayout == null ? null : info.windowLayout.windowLayoutAffinity;
final int intentFlags = intent == null ? 0 : intent.getFlags();
if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
@@ -3410,6 +3415,9 @@ class Task extends WindowContainer<WindowContainer> {
pw.println();
}
}
if (mWindowLayoutAffinity != null) {
pw.print(prefix); pw.print("windowLayoutAffinity="); pw.println(mWindowLayoutAffinity);
}
if (voiceSession != null || voiceInteractor != null) {
pw.print(prefix); pw.print("VOICE: session=0x");
pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
@@ -3591,6 +3599,9 @@ class Task extends WindowContainer<WindowContainer> {
} else if (rootAffinity != null) {
out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
}
if (mWindowLayoutAffinity != null) {
out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
}
out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
@@ -3748,6 +3759,7 @@ class Task extends WindowContainer<WindowContainer> {
String affinity = null;
String rootAffinity = null;
boolean hasRootAffinity = false;
String windowLayoutAffinity = null;
boolean rootHasReset = false;
boolean autoRemoveRecents = false;
boolean askedCompatMode = false;
@@ -3800,6 +3812,9 @@ class Task extends WindowContainer<WindowContainer> {
rootAffinity = attrValue;
hasRootAffinity = true;
break;
case ATTR_WINDOW_LAYOUT_AFFINITY:
windowLayoutAffinity = attrValue;
break;
case ATTR_ROOTHASRESET:
rootHasReset = Boolean.parseBoolean(attrValue);
break;
@@ -3955,6 +3970,7 @@ class Task extends WindowContainer<WindowContainer> {
realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
task.setBounds(lastNonFullscreenBounds);
task.mWindowLayoutAffinity = windowLayoutAffinity;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
task.addChild(activities.get(activityNdx));

View File

@@ -37,8 +37,8 @@ import android.graphics.Rect;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.LocalServices;
import com.android.server.pm.PackageList;
@@ -72,6 +72,10 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
ComponentName.createRelative("com.android.foo", ".BarActivity");
private static final ComponentName ALTERNATIVE_COMPONENT =
ComponentName.createRelative("com.android.foo", ".AlternativeBarActivity");
private static final String TEST_WINDOW_LAYOUT_AFFINITY = "135:.Affinity";
private static final String TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY =
"246:.AlternativeAffinity";
private static final String TEST_DIFFERENT_AFFINITY_WITH_SAME_UID = "135:.DifferentAffinity";
private static final int TEST_WINDOWING_MODE = WINDOWING_MODE_FREEFORM;
private static final Rect TEST_BOUNDS = new Rect(100, 200, 300, 400);
@@ -99,11 +103,12 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
public void setUp() throws Exception {
mPersisterQueue = new TestPersisterQueue();
final File cacheFolder = InstrumentationRegistry.getContext().getCacheDir();
final File cacheFolder =
InstrumentationRegistry.getInstrumentation().getContext().getCacheDir();
mFolder = new File(cacheFolder, "launch_params_tests");
deleteRecursively(mFolder);
mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
mDisplayUniqueId = "test:" + sNextUniqueId++;
mTestDisplay = new TestDisplayContent.Builder(mService, 1000, 1500)
.setUniqueId(mDisplayUniqueId).build();
when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
@@ -210,6 +215,61 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
assertTrue("Result should be empty.", mResult.isEmpty());
}
@Test
public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_NoPreviousRecord() {
mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@Test
public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_HasOldPreviousRecord()
throws Exception {
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTaskWithDifferentComponent);
Thread.sleep(1); // Sleep 1ms so that the timestamp can for sure increase.
mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@Test
public void testReturnsEmptyLaunchParamsUidInLaunchAffinityMismatch() {
mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_DIFFERENT_AFFINITY_WITH_SAME_UID;
mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertTrue("Result must be empty.", mResult.isEmpty());
}
@Test
public void testReturnsEmptyLaunchParamsWindowLayoutAffinityMismatch() {
mTestTask.affinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY;
mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertTrue("Result must be empty.", mResult.isEmpty());
}
@Test
public void testSavesAndRestoresLaunchParamsAcrossInstances() {
mTarget.saveTask(mTestTask);
@@ -227,6 +287,52 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@Test
public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_NoPreviousRecord() {
mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mPersisterQueue.flush();
final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
mUserFolderGetter);
target.onSystemReady();
target.onUnlockUser(TEST_USER_ID);
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@Test
public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_HasOldPreviousRecord()
throws Exception {
mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTaskWithDifferentComponent);
mPersisterQueue.flush();
// Sleep 1s because many file systems only save last modified time as precise as 1s so we
// can for sure know the last modified time is different.
Thread.sleep(1000);
mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
mTarget.saveTask(mTestTask);
mPersisterQueue.flush();
final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
mUserFolderGetter);
target.onSystemReady();
target.onUnlockUser(TEST_USER_ID);
target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
assertEquals(TEST_BOUNDS, mResult.mBounds);
}
@Test
public void testClearsRecordsOfTheUserOnUserCleanUp() {
mTarget.saveTask(mTestTask);