diff options
40 files changed, 441 insertions, 297 deletions
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index 783c8c45d603..be5a5bf6c35a 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -282,14 +282,31 @@ public final class Sm { StorageManager.DEBUG_VIRTUAL_DISK); } - public void runIsolatedStorage() throws RemoteException { - final boolean enableIsolatedStorage = Boolean.parseBoolean(nextArg()); + public void runIsolatedStorage() { + final int value; + final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON + | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; + switch (nextArg()) { + case "on": + case "true": + value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON; + break; + case "off": + value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; + break; + case "default": + case "false": + value = 0; + break; + default: + return; + } + // Toggling isolated-storage state will result in a device reboot. So to avoid this command // from erroring out (DeadSystemException), call setDebugFlags() in a separate thread. new Thread(() -> { try { - mSm.setDebugFlags(enableIsolatedStorage ? StorageManager.DEBUG_ISOLATED_STORAGE : 0, - StorageManager.DEBUG_ISOLATED_STORAGE); + mSm.setDebugFlags(value, mask); } catch (RemoteException e) { Log.e(TAG, "Encountered an error!", e); } @@ -334,7 +351,7 @@ public final class Sm { System.err.println(""); System.err.println(" sm set-emulate-fbe [true|false]"); System.err.println(""); - System.err.println(" sm set-isolated-storage [true|false]"); + System.err.println(" sm set-isolated-storage [on|off|default]"); System.err.println(""); return 1; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 9ee078696196..2d0e8bcae5f1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2627,6 +2627,8 @@ message NativeProcessMemoryState { * * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie) * and for selected native processes. + * + * Pulling this atom resets high-water mark counters for all processes. */ message ProcessMemoryHighWaterMark { // The uid if available. -1 means not available. diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 14f2de0b1a48..13579d283de0 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -49,7 +49,7 @@ const int FIELD_ID_TIME_BASE = 9; const int FIELD_ID_BUCKET_SIZE = 10; const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12; -const int FIELD_ID_IS_ACTIVE = 13; +const int FIELD_ID_IS_ACTIVE = 14; // for CountMetricDataWrapper const int FIELD_ID_DATA = 1; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 7797bd98961d..0425671b6367 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -48,7 +48,7 @@ const int FIELD_ID_TIME_BASE = 9; const int FIELD_ID_BUCKET_SIZE = 10; const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12; -const int FIELD_ID_IS_ACTIVE = 13; +const int FIELD_ID_IS_ACTIVE = 14; // for DurationMetricDataWrapper const int FIELD_ID_DATA = 1; // for DurationMetricData diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 31a4361f353d..ea125d02f337 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -44,7 +44,7 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_ID = 1; const int FIELD_ID_EVENT_METRICS = 4; -const int FIELD_ID_IS_ACTIVE = 13; +const int FIELD_ID_IS_ACTIVE = 14; // for EventMetricDataWrapper const int FIELD_ID_DATA = 1; // for EventMetricData diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 03e42ce76460..98a33f5280e6 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -49,7 +49,7 @@ const int FIELD_ID_TIME_BASE = 9; const int FIELD_ID_BUCKET_SIZE = 10; const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12; -const int FIELD_ID_IS_ACTIVE = 13; +const int FIELD_ID_IS_ACTIVE = 14; // for GaugeMetricDataWrapper const int FIELD_ID_DATA = 1; const int FIELD_ID_SKIPPED = 2; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 1f22a6af3a3d..7475b53c0a84 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -52,7 +52,7 @@ const int FIELD_ID_TIME_BASE = 9; const int FIELD_ID_BUCKET_SIZE = 10; const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11; const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12; -const int FIELD_ID_IS_ACTIVE = 13; +const int FIELD_ID_IS_ACTIVE = 14; // for ValueMetricDataWrapper const int FIELD_ID_DATA = 1; const int FIELD_ID_SKIPPED = 2; diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index a6f27c8aa535..5a87e46097ae 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -220,7 +220,9 @@ message StatsLogReport { optional DimensionsValue dimensions_path_in_condition = 12; - optional bool is_active = 13; + // DO NOT USE field 13. + + optional bool is_active = 14; } message UidMapping { diff --git a/config/dirty-image-objects b/config/dirty-image-objects index 9b4d199dc723..9e2230b288c8 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -44,7 +44,6 @@ java.util.function.ToIntFunction sun.misc.FormattedFloatingDecimal java.util.stream.IntStream android.icu.util.TimeZone -libcore.io.DropBox org.apache.harmony.luni.internal.util.TimezoneGetter dalvik.system.SocketTagger dalvik.system.CloseGuard @@ -137,7 +136,6 @@ java.lang.CharSequence android.icu.util.ULocale dalvik.system.BaseDexClassLoader android.icu.text.BreakIterator -libcore.io.EventLogger libcore.net.NetworkSecurityPolicy android.icu.text.UnicodeSet com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder diff --git a/config/preloaded-classes b/config/preloaded-classes index 30959256c922..c8a2a9c19b28 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -6137,12 +6137,6 @@ libcore.io.BufferIterator libcore.io.ClassPathURLStreamHandler libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection$1 -libcore.io.DropBox -libcore.io.DropBox$DefaultReporter -libcore.io.DropBox$Reporter -libcore.io.EventLogger -libcore.io.EventLogger$DefaultReporter -libcore.io.EventLogger$Reporter libcore.io.ForwardingOs libcore.io.IoBridge libcore.io.IoTracker diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index fe9b1ffb378f..1b45d172cb89 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -86,7 +86,6 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Debug; -import android.os.DropBoxManager; import android.os.Environment; import android.os.FileUtils; import android.os.GraphicsEnvironment; @@ -166,8 +165,6 @@ import dalvik.system.CloseGuard; import dalvik.system.VMDebug; import dalvik.system.VMRuntime; -import libcore.io.DropBox; -import libcore.io.EventLogger; import libcore.io.ForwardingOs; import libcore.io.IoUtils; import libcore.io.Os; @@ -6726,9 +6723,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - // add dropbox logging to libcore - DropBox.setReporter(new DropBoxReporter()); - ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { synchronized (mResourcesManager) { @@ -6782,39 +6776,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - private static class EventLoggingReporter implements EventLogger.Reporter { - @Override - public void report (int code, Object... list) { - EventLog.writeEvent(code, list); - } - } - - private static class DropBoxReporter implements DropBox.Reporter { - - private DropBoxManager dropBox; - - public DropBoxReporter() {} - - @Override - public void addData(String tag, byte[] data, int flags) { - ensureInitialized(); - dropBox.addData(tag, data, flags); - } - - @Override - public void addText(String tag, String data) { - ensureInitialized(); - dropBox.addText(tag, data); - } - - private synchronized void ensureInitialized() { - if (dropBox == null) { - dropBox = currentActivityThread().getApplication() - .getSystemService(DropBoxManager.class); - } - } - } - private static class AndroidOs extends ForwardingOs { /** * Install selective syscall interception. For example, this is used to @@ -6904,9 +6865,6 @@ public final class ActivityThread extends ClientTransactionHandler { Environment.initForCurrentUser(); - // Set the reporter for event logging in libcore - EventLogger.setReporter(new EventLoggingReporter()); - // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 567efa7b7afb..428d9e156e37 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -49,6 +49,7 @@ import android.util.Slog; import android.webkit.MimeTypeMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.SizedInputStream; import libcore.io.IoUtils; @@ -110,8 +111,6 @@ public class FileUtils { public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+"); } - private static final File[] EMPTY = new File[0]; - // non-final so it can be toggled by Robolectric's ShadowFileUtils private static boolean sEnableCopyOptimizations = true; @@ -1164,35 +1163,20 @@ public class FileUtils { /** {@hide} */ public static @NonNull String[] listOrEmpty(@Nullable File dir) { - if (dir == null) return EmptyArray.STRING; - final String[] res = dir.list(); - if (res != null) { - return res; - } else { - return EmptyArray.STRING; - } + return (dir != null) ? ArrayUtils.defeatNullable(dir.list()) + : EmptyArray.STRING; } /** {@hide} */ public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) { - if (dir == null) return EMPTY; - final File[] res = dir.listFiles(); - if (res != null) { - return res; - } else { - return EMPTY; - } + return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles()) + : ArrayUtils.EMPTY_FILE; } /** {@hide} */ public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) { - if (dir == null) return EMPTY; - final File[] res = dir.listFiles(filter); - if (res != null) { - return res; - } else { - return EMPTY; - } + return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles(filter)) + : ArrayUtils.EMPTY_FILE; } /** {@hide} */ diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index bdc776dd3702..cbcf600f0fb8 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -25,10 +25,15 @@ import android.util.MutableInt; import com.android.internal.annotations.GuardedBy; +import libcore.util.HexEncoding; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; - /** * Gives access to the system properties store. The system properties * store contains a list of string key-value pairs. @@ -232,6 +237,27 @@ public class SystemProperties { native_report_sysprop_change(); } + /** + * Return a {@code SHA-1} digest of the given keys and their values as a + * hex-encoded string. The ordering of the incoming keys doesn't change the + * digest result. + * + * @hide + */ + public static @NonNull String digestOf(@NonNull String... keys) { + Arrays.sort(keys); + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-1"); + for (String key : keys) { + final String item = key + "=" + get(key) + "\n"; + digest.update(item.getBytes(StandardCharsets.UTF_8)); + } + return HexEncoding.encodeToString(digest.digest()).toLowerCase(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + private SystemProperties() { } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index d315383a5775..8b1d3c6c839f 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -226,7 +226,9 @@ public class StorageManager { /** {@hide} */ public static final int DEBUG_VIRTUAL_DISK = 1 << 5; /** {@hide} */ - public static final int DEBUG_ISOLATED_STORAGE = 1 << 6; + public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6; + /** {@hide} */ + public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7; /** {@hide} */ public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index be304b554043..7f135fb8abb4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12964,6 +12964,11 @@ public final class Settings { public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED = "content_capture_service_explicitly_enabled"; + /** {@hide} */ + public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local"; + /** {@hide} */ + public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote"; + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ddf119097027..4574f636306c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10823,6 +10823,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return onTextContextMenuItem(ID_PASTE); } break; + case KeyEvent.KEYCODE_INSERT: + if (canCopy()) { + return onTextContextMenuItem(ID_COPY); + } + break; + } + } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { + // Handle Shift-only shortcuts. + switch (keyCode) { + case KeyEvent.KEYCODE_FORWARD_DEL: + if (canCut()) { + return onTextContextMenuItem(ID_CUT); + } + break; + case KeyEvent.KEYCODE_INSERT: + if (canPaste()) { + return onTextContextMenuItem(ID_PASTE); + } + break; } } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) { // Handle Ctrl-Shift shortcuts. diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index f669e94c1779..226b8db1540b 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -24,6 +24,7 @@ import dalvik.system.VMRuntime; import libcore.util.EmptyArray; +import java.io.File; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; @@ -42,6 +43,8 @@ public class ArrayUtils { private static final int CACHE_SIZE = 73; private static Object[] sCache = new Object[CACHE_SIZE]; + public static final File[] EMPTY_FILE = new File[0]; + private ArrayUtils() { /* cannot be instantiated */ } public static byte[] newUnpaddedByteArray(int minLen) { @@ -645,6 +648,10 @@ public class ArrayUtils { return (val != null) ? val : EmptyArray.STRING; } + public static @NonNull File[] defeatNullable(@Nullable File[] val) { + return (val != null) ? val : EMPTY_FILE; + } + /** * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds. * diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index a398e498a301..33b26899fe81 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -331,11 +331,13 @@ bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const { return false; } - if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) { + int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0; + if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) { close(new_fd); - *error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s", + *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s", fd, new_fd, + dupFlags, file_path.c_str(), strerror(errno)); return false; diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java index 985fa4f3cfc8..2b841269e5ae 100644 --- a/core/tests/coretests/src/android/os/OsTests.java +++ b/core/tests/coretests/src/android/os/OsTests.java @@ -33,7 +33,6 @@ public class OsTests { suite.addTestSuite(MessageQueueTest.class); suite.addTestSuite(MessengerTest.class); suite.addTestSuite(PatternMatcherTest.class); - suite.addTestSuite(SystemPropertiesTest.class); return suite; } diff --git a/core/tests/coretests/src/android/os/SystemPropertiesTest.java b/core/tests/coretests/src/android/os/SystemPropertiesTest.java deleted file mode 100644 index 25868ce0b702..000000000000 --- a/core/tests/coretests/src/android/os/SystemPropertiesTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import static junit.framework.Assert.assertEquals; -import junit.framework.TestCase; - -import android.os.SystemProperties; -import android.test.suitebuilder.annotation.SmallTest; - -public class SystemPropertiesTest extends TestCase { - private static final String KEY = "com.android.frameworks.coretests"; - @SmallTest - public void testProperties() throws Exception { - if (false) { - String value; - - SystemProperties.set(KEY, ""); - value = SystemProperties.get(KEY, "default"); - assertEquals("default", value); - - SystemProperties.set(KEY, "AAA"); - value = SystemProperties.get(KEY, "default"); - assertEquals("AAA", value); - - value = SystemProperties.get(KEY); - assertEquals("AAA", value); - - SystemProperties.set(KEY, ""); - value = SystemProperties.get(KEY, "default"); - assertEquals("default", value); - - value = SystemProperties.get(KEY); - assertEquals("", value); - } - } -} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 29bc4c4af5d2..6d1aae12d858 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -543,7 +543,9 @@ public class SettingsBackupTest { Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, - Settings.Global.BACKUP_MULTI_USER_ENABLED); + Settings.Global.BACKUP_MULTI_USER_ENABLED, + Settings.Global.ISOLATED_STORAGE_LOCAL, + Settings.Global.ISOLATED_STORAGE_REMOTE); private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS = newHashSet( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java index 933e54e840c5..928351e7de8c 100644 --- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java +++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java @@ -16,13 +16,13 @@ package android.os; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; -import android.os.SystemProperties; -import android.test.suitebuilder.annotation.SmallTest; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public class SystemPropertiesTest extends TestCase { private static final String KEY = "sys.testkey"; @@ -188,4 +188,25 @@ public class SystemPropertiesTest extends TestCase { fail("InterruptedException"); } } + + @SmallTest + public void testDigestOf() { + final String empty = SystemProperties.digestOf(); + final String finger = SystemProperties.digestOf("ro.build.fingerprint"); + final String fingerBrand = SystemProperties.digestOf( + "ro.build.fingerprint", "ro.product.brand"); + final String brandFinger = SystemProperties.digestOf( + "ro.product.brand", "ro.build.fingerprint"); + + // Shouldn't change over time + assertTrue(Objects.equals(finger, SystemProperties.digestOf("ro.build.fingerprint"))); + + // Different properties means different results + assertFalse(Objects.equals(empty, finger)); + assertFalse(Objects.equals(empty, fingerBrand)); + assertFalse(Objects.equals(finger, fingerBrand)); + + // Same properties means same result + assertTrue(Objects.equals(fingerBrand, brandFinger)); + } } diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index bd0019f0f1d8..bfc05fa4e3a4 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -39,7 +39,7 @@ interface ISession { void destroy(); // These commands are for the TransportPerformer - void setMetadata(in MediaMetadata metadata); + void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); void setPlaybackState(in PlaybackState state); void setQueue(in ParceledListSlice queue); void setQueueTitle(CharSequence title); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index d43cd309d157..8962bb7fb4c4 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -30,6 +30,7 @@ import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; +import android.media.session.MediaSessionManager.RemoteUserInfo; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -40,7 +41,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; -import android.media.session.MediaSessionManager.RemoteUserInfo; import android.service.media.MediaBrowserService; import android.text.TextUtils; import android.util.Log; @@ -434,11 +434,21 @@ public final class MediaSession { * @see android.media.MediaMetadata.Builder#putBitmap */ public void setMetadata(@Nullable MediaMetadata metadata) { + long duration = -1; + int fields = 0; + MediaDescription description = null; if (metadata != null) { metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build(); + if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { + duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION); + } + fields = metadata.size(); + description = metadata.getDescription(); } + String metadataDescription = "size=" + fields + ", description=" + description; + try { - mBinder.setMetadata(metadata); + mBinder.setMetadata(metadata, duration, metadataDescription); } catch (RemoteException e) { Log.wtf(TAG, "Dead object in setPlaybackState.", e); } diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java index 5c126b1d839b..120acd3bcc52 100644 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java @@ -263,6 +263,10 @@ public class InputMethodPreference extends RestrictedSwitchPreference implements // The user canceled to enable a 3rd party IME. setCheckedInternal(false); }); + builder.setOnCancelListener((dialog) -> { + // The user canceled to enable a 3rd party IME. + setCheckedInternal(false); + }); mDialog = builder.create(); mDialog.show(); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 581d4350d28d..6c62725bb5bd 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -57,6 +57,7 @@ import android.app.KeyguardManager; import android.app.admin.SecurityLog; import android.app.usage.StorageStatsManager; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -777,6 +778,18 @@ class StorageManagerService extends IStorageManager.Stub } }); refreshZramSettings(); + + // Toggle isolated-enable system property in response to settings + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE), + false /*notifyForDescendants*/, + new ContentObserver(null /* current thread */) { + @Override + public void onChange(boolean selfChange) { + refreshIsolatedStorageSettings(); + } + }); + refreshIsolatedStorageSettings(); } /** @@ -802,6 +815,32 @@ class StorageManagerService extends IStorageManager.Stub } } + private void refreshIsolatedStorageSettings() { + final int local = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ISOLATED_STORAGE_LOCAL, 0); + final int remote = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ISOLATED_STORAGE_REMOTE, 0); + + // Walk down precedence chain; we prefer local settings first, then + // remote settings, before finally falling back to hard-coded default. + final boolean res; + if (local == -1) { + res = false; + } else if (local == 1) { + res = true; + } else if (remote == -1) { + res = false; + } else if (remote == 1) { + res = true; + } else { + res = false; + } + + Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " + + remote + " resolved to " + res); + SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res)); + } + /** * MediaProvider has a ton of code that makes assumptions about storage * paths never changing, so we outright kill them to pick up new state. @@ -2208,18 +2247,22 @@ class StorageManagerService extends IStorageManager.Stub } } - if ((mask & StorageManager.DEBUG_ISOLATED_STORAGE) != 0) { - final boolean enabled = (flags & StorageManager.DEBUG_ISOLATED_STORAGE) != 0; + if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON + | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) { + final int value; + if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) { + value = 1; + } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) { + value = -1; + } else { + value = 0; + } final long token = Binder.clearCallingIdentity(); try { - SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, - Boolean.toString(enabled)); - - // Some of the storage related permissions get fiddled with during - // package scanning. So, delete the package cache to force PackageManagerService - // to do package scanning. - FileUtils.deleteContents(Environment.getPackageCacheDirectory()); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ISOLATED_STORAGE_LOCAL, value); + refreshIsolatedStorageSettings(); // Perform hard reboot to kick policy into place mContext.getSystemService(PowerManager.class).reboot(null); @@ -3758,6 +3801,8 @@ class StorageManagerService extends IStorageManager.Stub pw.println(); pw.println("Primary storage UUID: " + mPrimaryStorageUuid); + + pw.println(); final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize(); if (pair == null) { pw.println("Internal storage total size: N/A"); @@ -3770,8 +3815,18 @@ class StorageManagerService extends IStorageManager.Stub pw.print(DataUnit.MEBIBYTES.toBytes(pair.second)); pw.println(" MiB)"); } + + pw.println(); pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers)); pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); + + final ContentResolver cr = mContext.getContentResolver(); + pw.println(); + pw.println("Isolated storage, local feature flag: " + + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0)); + pw.println("Isolated storage, remote feature flag: " + + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0)); + pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage()); } synchronized (mObbMounts) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 8751d24bcf67..5afb90d681af 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1964,7 +1964,8 @@ public final class ActiveServices { + " type=" + resolvedType + " callingUid=" + callingUid); userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false, - ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null); + ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", + callingPackage); ServiceMap smap = getServiceMapLocked(userId); final ComponentName comp; diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index c938f5eec94d..c1f3468627cb 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -20,10 +20,10 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioSystem; -import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; @@ -36,7 +36,6 @@ import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; -import android.media.AudioAttributes; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -94,8 +93,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private PendingIntent mLaunchIntent; // TransportPerformer fields - private Bundle mExtras; + // Note: Avoid unparceling the bundle inside MediaMetadata since unparceling in system process + // may result in throwing an exception. private MediaMetadata mMetadata; private PlaybackState mPlaybackState; private ParceledListSlice mQueue; @@ -117,6 +117,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private boolean mIsActive = false; private boolean mDestroyed = false; + private long mDuration; + private String mMetadataDescription; + public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) { mOwnerPid = ownerPid; @@ -451,7 +454,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { pw.println(indent + "audioAttrs=" + mAudioAttrs); pw.println(indent + "volumeType=" + mVolumeType + ", controlType=" + mVolumeControlType + ", max=" + mMaxVolume + ", current=" + mCurrentVolume); - pw.println(indent + "metadata:" + getShortMetadataString()); + pw.println(indent + "metadata: " + mMetadataDescription); pw.println(indent + "queueTitle=" + mQueueTitle + ", size=" + (mQueue == null ? 0 : mQueue.getList().size())); } @@ -494,13 +497,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { }); } - private String getShortMetadataString() { - int fields = mMetadata == null ? 0 : mMetadata.size(); - MediaDescription description = mMetadata == null ? null : mMetadata - .getDescription(); - return "size=" + fields + ", description=" + description; - } - private void logCallbackException( String msg, ISessionControllerCallbackHolder holder, Exception e) { Log.v(TAG, msg + ", this=" + this + ", callback package=" + holder.mPackageName @@ -670,12 +666,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private PlaybackState getStateWithUpdatedPosition() { PlaybackState state; - long duration = -1; + long duration; synchronized (mLock) { state = mPlaybackState; - if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { - duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); - } + duration = mDuration; } PlaybackState result = null; if (state != null) { @@ -793,7 +787,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void setMetadata(MediaMetadata metadata) { + public void setMetadata(MediaMetadata metadata, long duration, String metadataDescription) { synchronized (mLock) { MediaMetadata temp = metadata == null ? null : new MediaMetadata.Builder(metadata) .build(); @@ -804,6 +798,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { temp.size(); } mMetadata = temp; + mDuration = duration; + mMetadataDescription = metadataDescription; } mHandler.post(MessageHandler.MSG_UPDATE_METADATA); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ae29180a8043..2826e7b8fe2b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -323,6 +323,7 @@ import dalvik.system.VMRuntime; import libcore.io.IoUtils; import libcore.util.EmptyArray; +import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -591,12 +592,6 @@ public class PackageManagerService extends IPackageManager.Stub public static final int REASON_LAST = REASON_SHARED; /** - * Version number for the package parser cache. Increment this whenever the format or - * extent of cached data changes. See {@code PackageParser#setCacheDir}. - */ - private static final String PACKAGE_PARSER_CACHE_VERSION = "1"; - - /** * Whether the package parser cache is enabled. */ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; @@ -2329,7 +2324,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - mCacheDir = preparePackageParserCache(mIsUpgrade); + mCacheDir = preparePackageParserCache(); // Set flag to monitor and not change apk file paths when // scanning install directories. @@ -3196,7 +3191,7 @@ public class PackageManagerService extends IPackageManager.Stub setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr()); } - private static File preparePackageParserCache(boolean isUpgrade) { + private static @Nullable File preparePackageParserCache() { if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { return null; } @@ -3217,17 +3212,25 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - // If this is a system upgrade scenario, delete the contents of the package cache dir. - // This also serves to "GC" unused entries when the package cache version changes (which - // can only happen during upgrades). - if (isUpgrade) { - FileUtils.deleteContents(cacheBaseDir); - } + // There are several items that need to be combined together to safely + // identify cached items. In particular, changing the value of certain + // feature flags should cause us to invalidate any caches. + final String cacheName = SystemProperties.digestOf( + "ro.build.fingerprint", + "persist.sys.isolated_storage"); + // Reconcile cache directories, keeping only what we'd actually use. + for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { + if (Objects.equals(cacheName, cacheDir.getName())) { + Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); + } else { + Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); + FileUtils.deleteContentsAndDir(cacheDir); + } + } - // Return the versioned package cache directory. This is something like - // "/data/system/package_cache/1" - File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION); + // Return the versioned package cache directory. + File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); if (cacheDir == null) { // Something went wrong. Attempt to delete everything and return. @@ -3253,7 +3256,7 @@ public class PackageManagerService extends IPackageManager.Stub File frameworkDir = new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); - cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION); + cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d1d5818b8c46..4f20590a47ff 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2691,10 +2691,12 @@ public class UserManagerService extends IUserManager.Stub { if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) { // If we're not adding a guest/demo user or a managed profile and the limit has // been reached, cannot add a user. + Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached."); return null; } // If we're adding a guest and there already exists one, bail. if (isGuest && findCurrentGuestUser() != null) { + Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists."); return null; } // In legacy mode, restricted profile's parent can only be the owner user @@ -2937,13 +2939,26 @@ public class UserManagerService extends IUserManager.Stub { final UserData userData; int currentUser = ActivityManager.getCurrentUser(); if (currentUser == userHandle) { - Log.w(LOG_TAG, "Current user cannot be removed"); + Log.w(LOG_TAG, "Current user cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userHandle); - if (userHandle == 0 || userData == null || mRemovingUserIds.get(userHandle)) { + if (userHandle == UserHandle.USER_SYSTEM) { + Log.e(LOG_TAG, "System user cannot be removed."); + return false; + } + + if (userData == null) { + Log.e(LOG_TAG, String.format( + "Cannot remove user %d, invalid user id provided.", userHandle)); + return false; + } + + if (mRemovingUserIds.get(userHandle)) { + Log.e(LOG_TAG, String.format( + "User %d is already scheduled for removal.", userHandle)); return false; } @@ -2962,7 +2977,7 @@ public class UserManagerService extends IUserManager.Stub { try { mAppOpsService.removeUser(userHandle); } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e); + Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e); } if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID @@ -2986,6 +3001,7 @@ public class UserManagerService extends IUserManager.Stub { } }); } catch (RemoteException e) { + Log.w(LOG_TAG, "Failed to stop user during removal.", e); return false; } return res == ActivityManager.USER_OP_SUCCESS; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index f0ebb7512015..4ec8b87be3ac 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -74,6 +74,7 @@ import android.os.StatsDimensionsValue; import android.os.StatsLogEventWrapper; import android.os.SynchronousResultReceiver; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Temperature; import android.os.UserHandle; import android.os.UserManager; @@ -1140,7 +1141,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeLong(rssHighWaterMarkInBytes); pulledData.add(e); } - // TODO(b/119598534): Reset HWM counters here. + // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes. + SystemProperties.set("sys.rss_hwm_reset.on", "1"); } private void pullBinderCallsStats( diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 57bfc2979636..36701ea599dc 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -73,6 +73,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; @@ -1339,6 +1341,21 @@ class ActivityStarter { voiceInteractor); final int preferredWindowingMode = mLaunchParams.mWindowingMode; + computeLaunchingTaskFlags(); + + computeSourceStack(); + + mIntent.setFlags(mLaunchFlags); + + ActivityRecord reusedActivity = getReusableIntentActivity(); + + mSupervisor.getLaunchParamsController().calculate( + reusedActivity != null ? reusedActivity.getTaskRecord() : mInTask, + r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams); + mPreferredDisplayId = + mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId + : DEFAULT_DISPLAY; + // Do not start home activity if it cannot be launched on preferred display. We are not // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might // fallback to launch on other displays. @@ -1348,14 +1365,6 @@ class ActivityStarter { return START_CANCELED; } - computeLaunchingTaskFlags(); - - computeSourceStack(); - - mIntent.setFlags(mLaunchFlags); - - ActivityRecord reusedActivity = getReusableIntentActivity(); - if (reusedActivity != null) { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and @@ -1651,14 +1660,13 @@ class ActivityStarter { mLaunchParams.reset(); + // Preferred display id is the only state we need for now and it could be updated again + // after we located a reusable task (which might be resided in another display). mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r, - sourceRecord, options, mLaunchParams); - - if (mLaunchParams.hasPreferredDisplay()) { - mPreferredDisplayId = mLaunchParams.mPreferredDisplayId; - } else { - mPreferredDisplayId = DEFAULT_DISPLAY; - } + sourceRecord, options, PHASE_DISPLAY, mLaunchParams); + mPreferredDisplayId = + mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId + : DEFAULT_DISPLAY; mLaunchMode = r.launchMode; @@ -2502,14 +2510,9 @@ class ActivityStarter { if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) || mPreferredDisplayId != DEFAULT_DISPLAY) { - // We don't pass in the default display id into the get launch stack call so it can do a - // full resolution. - mLaunchParams.mPreferredDisplayId = - mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY; final boolean onTop = aOptions == null || !aOptions.getAvoidMoveToFront(); final ActivityStack stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams); - mLaunchParams.mPreferredDisplayId = mPreferredDisplayId; return stack; } // Otherwise handle adjacent launch. diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index 09475777cb6e..59c02f736513 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; @@ -74,7 +75,7 @@ class LaunchParamsController { * @param result The resulting params. */ void calculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, LaunchParams result) { + ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) { result.reset(); if (task != null || activity != null) { @@ -89,7 +90,7 @@ class LaunchParamsController { mTmpResult.reset(); final LaunchParamsModifier modifier = mModifiers.get(i); - switch(modifier.onCalculate(task, layout, activity, source, options, mTmpCurrent, + switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent, mTmpResult)) { case RESULT_SKIP: // Do not apply any results when we are told to skip @@ -125,7 +126,7 @@ class LaunchParamsController { boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options) { - calculate(task, layout, activity, source, options, mTmpParams); + calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams); // No changes, return. if (mTmpParams.isEmpty()) { @@ -259,6 +260,25 @@ class LaunchParamsController { */ int RESULT_CONTINUE = 2; + @Retention(RetentionPolicy.SOURCE) + @IntDef({PHASE_DISPLAY, PHASE_WINDOWING_MODE, PHASE_BOUNDS}) + @interface Phase {} + + /** + * Stops once we are done with preferred display calculation. + */ + int PHASE_DISPLAY = 0; + + /** + * Stops once we are done with windowing mode calculation. + */ + int PHASE_WINDOWING_MODE = 1; + + /** + * Stops once we are done with window bounds calculation. + */ + int PHASE_BOUNDS = 2; + /** * Returns the launch params that the provided activity launch params should be overridden * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1) @@ -277,6 +297,7 @@ class LaunchParamsController { * launched should have this be non-null. * @param source the Activity that launched a new task. Could be {@code null}. * @param options {@link ActivityOptions} used to start the activity with. + * @param phase the calculation phase, see {@link LaunchParamsModifier.Phase} * @param currentParams launching params after the process of last {@link * LaunchParamsModifier}. * @param outParams the result params to be set. @@ -284,7 +305,7 @@ class LaunchParamsController { */ @Result int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, LaunchParams currentParams, - LaunchParams outParams); + ActivityRecord source, ActivityOptions options, @Phase int phase, + LaunchParams currentParams, LaunchParams outParams); } } diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 84a32fc44c53..d0144fdf670a 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1615,33 +1615,35 @@ class RootActivityContainer extends ConfigurationContainer return candidateTask.getStack(); } + int windowingMode; + if (launchParams != null) { + // When launch params is not null, we always defer to its windowing mode. Sometimes + // it could be unspecified, which indicates it should inherit windowing mode from + // display. + windowingMode = launchParams.mWindowingMode; + } else { + windowingMode = options != null ? options.getLaunchWindowingMode() + : r.getWindowingMode(); + } + windowingMode = activityDisplay.validateWindowingMode(windowingMode, r, candidateTask, + r.getActivityType()); + // Return the topmost valid stack on the display. for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) { final ActivityStack stack = activityDisplay.getChildAt(i); - if (isValidLaunchStack(stack, r)) { + if (isValidLaunchStack(stack, r, windowingMode)) { return stack; } } // If there is no valid stack on the external display - check if new dynamic stack will do. if (displayId != DEFAULT_DISPLAY) { - final int windowingMode; - if (launchParams != null) { - // When launch params is not null, we always defer to its windowing mode. Sometimes - // it could be unspecified, which indicates it should inherit windowing mode from - // display. - windowingMode = launchParams.mWindowingMode; - } else { - windowingMode = options != null ? options.getLaunchWindowingMode() - : r.getWindowingMode(); - } final int activityType = options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED ? options.getLaunchActivityType() : r.getActivityType(); return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/); } - Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId); return null; } @@ -1653,7 +1655,7 @@ class RootActivityContainer extends ConfigurationContainer } // TODO: Can probably be consolidated into getLaunchStack()... - private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) { + private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r, int windowingMode) { switch (stack.getActivityType()) { case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); @@ -1661,11 +1663,13 @@ class RootActivityContainer extends ConfigurationContainer } // There is a 1-to-1 relationship between stack and task when not in // primary split-windowing mode. - if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return false; - } else { - return r.supportsSplitScreenWindowingMode(); + if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && r.supportsSplitScreenWindowingMode() + && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || windowingMode == WINDOWING_MODE_UNDEFINED)) { + return true; } + return false; } int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 1fb7979fa3e2..5107b522c33b 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -51,6 +51,7 @@ import android.util.Slog; import android.view.Gravity; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; @@ -102,19 +103,27 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { mSupervisor = supervisor; } + @VisibleForTesting + int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, ActivityRecord activity, + ActivityRecord source, ActivityOptions options, LaunchParams currentParams, + LaunchParams outParams) { + return onCalculate(task, layout, activity, source, options, PHASE_BOUNDS, currentParams, + outParams); + } + @Override public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, - LaunchParams currentParams, LaunchParams outParams) { + int phase, LaunchParams currentParams, LaunchParams outParams) { initLogBuilder(task, activity); - final int result = calculate(task, layout, activity, source, options, currentParams, + final int result = calculate(task, layout, activity, source, options, phase, currentParams, outParams); outputLog(); return result; } private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, + ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams) { final ActivityRecord root; if (task != null) { @@ -145,6 +154,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { + display.getWindowingMode()); } + if (phase == PHASE_DISPLAY) { + return RESULT_CONTINUE; + } + // STEP 2: Resolve launch windowing mode. // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the // launch bounds from activity options, or size/gravity passed in layout. It also treats the @@ -247,6 +260,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { outParams.mWindowingMode = launchMode == display.getWindowingMode() ? WINDOWING_MODE_UNDEFINED : launchMode; + if (phase == PHASE_WINDOWING_MODE) { + return RESULT_CONTINUE; + } + // STEP 3: Determine final launch bounds based on resolved windowing mode and activity // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is // for all other windowing modes that's not freeform mode. One can read comments in @@ -288,12 +305,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { displayId = optionLaunchId; } - if (displayId == INVALID_DISPLAY && source != null) { - final int sourceDisplayId = source.getDisplayId(); - if (DEBUG) appendLog("display-from-source=" + sourceDisplayId); - displayId = sourceDisplayId; - } - ActivityStack stack = (displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null; if (stack != null) { @@ -301,6 +312,12 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { displayId = stack.mDisplayId; } + if (displayId == INVALID_DISPLAY && source != null) { + final int sourceDisplayId = source.getDisplayId(); + if (DEBUG) appendLog("display-from-source=" + sourceDisplayId); + displayId = sourceDisplayId; + } + if (displayId != INVALID_DISPLAY && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) { displayId = currentParams.mPreferredDisplayId; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f86d08965e32..e78c12cfcc78 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2168,11 +2168,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mTmpRect.inset(-delta, -delta); } region.set(mTmpRect); - region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); + cropRegionToStackBoundsIfNeeded(region); } else { // Not modal or full screen modal - getTouchableRegion(region, true /* forSurface */); + getTouchableRegion(region); } + // Translate to surface based coordinates. + region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top); return flags; } @@ -2809,16 +2811,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** Get the touchable region in global coordinates. */ void getTouchableRegion(Region outRegion) { - getTouchableRegion(outRegion, false /* forSurface */); - } - - /** If {@param forSuface} is {@code true}, the region will be translated to surface based. */ - private void getTouchableRegion(Region outRegion, boolean forSurface) { - if (inPinnedWindowingMode() && !isFocused()) { - outRegion.setEmpty(); - return; - } - final Rect frame = mWindowFrames.mFrame; switch (mTouchableInsets) { default: @@ -2833,18 +2825,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP break; case TOUCHABLE_INSETS_REGION: { outRegion.set(mGivenTouchableRegion); + outRegion.translate(frame.left, frame.top); break; } } cropRegionToStackBoundsIfNeeded(outRegion); - - if (forSurface) { - if (mTouchableInsets != TOUCHABLE_INSETS_REGION) { - outRegion.translate(-frame.left, -frame.top); - } - outRegion.getBounds(mTmpRect); - applyInsets(outRegion, mTmpRect, mAttrs.surfaceInsets); - } } private void cropRegionToStackBoundsIfNeeded(Region region) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b3afacbf15ec..bd3dfe96a67e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5968,9 +5968,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // If set, remove exclusive scopes from all other delegates if (exclusiveScopes != null && !exclusiveScopes.isEmpty()) { - for (Map.Entry<String, List<String>> entry : policy.mDelegationMap.entrySet()) { - final String currentPackage = entry.getKey(); - final List<String> currentScopes = entry.getValue(); + for (int i = policy.mDelegationMap.size() - 1; i >= 0; --i) { + final String currentPackage = policy.mDelegationMap.keyAt(i); + final List<String> currentScopes = policy.mDelegationMap.valueAt(i); if (!currentPackage.equals(delegatePackage)) { // Iterate through all other delegates @@ -5978,7 +5978,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // And if this delegate had some exclusive scopes which are now moved // to the new delegate, notify about its delegation changes. if (currentScopes.isEmpty()) { - policy.mDelegationMap.remove(currentPackage); + policy.mDelegationMap.removeAt(i); } sendDelegationChangedBroadcast(currentPackage, new ArrayList<>(currentScopes), userId); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 7c43cf3fba83..61e968d6da00 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -414,10 +414,10 @@ public class ActivityStarterTests extends ActivityTestsBase { .setActivityOptions(new SafeActivityOptions(options)) .execute(); - // verify that values are passed to the modifier. Values are passed twice -- once for + // verify that values are passed to the modifier. Values are passed thrice -- two for // setting initial state, another when task is created. - verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options), - any(), any()); + verify(modifier, times(3)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options), + anyInt(), any(), any()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index 3720c8566e74..8c3dec7f1e75 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -31,6 +31,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; @@ -89,10 +90,10 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0); final ActivityOptions options = mock(ActivityOptions.class); - mController.calculate(record.getTaskRecord(), layout, record, source, options, + mController.calculate(record.getTaskRecord(), layout, record, source, options, PHASE_BOUNDS, new LaunchParams()); verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record), - eq(source), eq(options), any(), any()); + eq(source), eq(options), anyInt(), any(), any()); } /** @@ -115,9 +116,9 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { mPersister.putLaunchParams(userId, name, expected); mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/, - null /*options*/, new LaunchParams()); - verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected), - any()); + null /*options*/, PHASE_BOUNDS, new LaunchParams()); + verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), + eq(expected), any()); } /** @@ -128,14 +129,15 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { final LaunchParamsModifier ignoredPositioner = mock(LaunchParamsModifier.class); final LaunchParamsModifier earlyExitPositioner = - (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE; + (task, layout, activity, source, options, phase, currentParams, outParams) + -> RESULT_DONE; mController.registerModifier(ignoredPositioner); mController.registerModifier(earlyExitPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, new LaunchParams()); - verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(), + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(), anyInt(), any(), any()); } @@ -152,20 +154,20 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { mController.registerModifier(firstPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, new LaunchParams()); - verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(), - any()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), + any(), any()); final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner); mController.registerModifier(secondPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, new LaunchParams()); - verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(), - any()); - verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(), - any()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), + any(), any()); + verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), + any(), any()); } /** @@ -187,9 +189,9 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { mController.registerModifier(positioner2); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, new LaunchParams()); + null /*options*/, PHASE_BOUNDS, new LaunchParams()); - verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(), + verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), eq(positioner2.getLaunchParams()), any()); } @@ -213,7 +215,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { final LaunchParams result = new LaunchParams(); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, result); + null /*options*/, PHASE_BOUNDS, result); assertEquals(result, positioner2.getLaunchParams()); } @@ -232,21 +234,42 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { // VR activities should always land on default display. mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/, - null /*source*/, null /*options*/, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result); assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId); // Otherwise, always lands on VR 2D display. final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build(); mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/, - null /*source*/, null /*options*/, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result); assertEquals(vr2dDisplayId, result.mPreferredDisplayId); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, result); + null /*options*/, PHASE_BOUNDS, result); assertEquals(vr2dDisplayId, result.mPreferredDisplayId); mService.mVr2dDisplayId = INVALID_DISPLAY; } + + /** + * Ensures that {@link LaunchParamsController} calculates to {@link PHASE_BOUNDS} phase by + * default. + */ + @Test + public void testCalculatePhase() { + final LaunchParamsModifier positioner = mock(LaunchParamsModifier.class); + mController.registerModifier(positioner); + + final ActivityRecord record = new ActivityBuilder(mService).build(); + final ActivityRecord source = new ActivityBuilder(mService).build(); + final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0); + final ActivityOptions options = mock(ActivityOptions.class); + + mController.calculate(record.getTaskRecord(), layout, record, source, options, PHASE_BOUNDS, + new LaunchParams()); + verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record), + eq(source), eq(options), eq(PHASE_BOUNDS), any(), any()); + } + /** * Ensures that {@link LaunchParamsModifier} requests specifying display id during * layout are honored. @@ -348,7 +371,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase { @Override public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, + ActivityRecord source, ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams) { outParams.set(mParams); return mReturnVal; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index fe632d4ab01e..0bd681bd69a2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -173,6 +173,23 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } @Test + public void testUsesTasksDisplayIdPriorToSourceIfSet() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay( + WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay); + ActivityRecord source = createSourceActivity(freeformDisplay); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTaskRecord(), + /* layout */ null, mActivity, source, /* options */ null, mCurrent, mResult)); + + assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId); + } + + @Test public void testUsesTaskDisplayIdIfSet() { final TestActivityDisplay freeformDisplay = createNewActivityDisplay( WINDOWING_MODE_FREEFORM); |