Merge "[Output Switcher] Fix SASS device update in U" into udc-dev
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5b6df1c..1850462 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1185,6 +1185,37 @@
"android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
/**
+ * Application level {@link android.content.pm.PackageManager.Property PackageManager
+ * .Property} for an app to inform the system that the app should be opted-out from the
+ * compatibility override that changes the min aspect ratio.
+ *
+ * <p>When this compat override is enabled the min aspect ratio given in the app's manifest can
+ * be overridden by the device manufacturer using their discretion to improve display
+ * compatibility unless the app's manifest value is higher. This treatment will also apply if
+ * no min aspect ratio value is provided in the manifest. These treatments can apply only in
+ * specific cases (e.g. device is in portrait) or each time the app is displayed on screen.
+ *
+ * <p>Setting this property to {@code false} informs the system that the app must be
+ * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+ * into the treatment.
+ *
+ * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE"
+ * android:value="true|false"/>
+ * </application>
+ * </pre>
+ * @hide
+ */
+ // TODO(b/279428317): Make this public API.
+ String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE =
+ "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index 55680ab..9595332 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -229,20 +229,36 @@
@Test
public void testGetIndex() {
- for (int index = 0; index < 2048; index++) {
- for (int type = FIRST; type <= LAST; type = type << 1) {
- final int id = InsetsSource.createId(null, index, type);
- assertEquals(index, InsetsSource.getIndex(id));
+ // Here doesn't iterate all the owners, or the test cannot be done before timeout.
+ for (int owner = 0; owner < 100; owner++) {
+ for (int index = 0; index < 2048; index++) {
+ for (int type = FIRST; type <= LAST; type = type << 1) {
+ final int id = InsetsSource.createId(owner, index, type);
+ final int indexFromId = InsetsSource.getIndex(id);
+ assertEquals("index and indexFromId must be the same. id=" + id
+ + ", owner=" + owner
+ + ", index=" + index
+ + ", type=" + type
+ + ", indexFromId=" + indexFromId + ".", index, indexFromId);
+ }
}
}
}
@Test
public void testGetType() {
- for (int index = 0; index < 2048; index++) {
- for (int type = FIRST; type <= LAST; type = type << 1) {
- final int id = InsetsSource.createId(null, index, type);
- assertEquals(type, InsetsSource.getType(id));
+ // Here doesn't iterate all the owners, or the test cannot be done before timeout.
+ for (int owner = 0; owner < 100; owner++) {
+ for (int index = 0; index < 2048; index++) {
+ for (int type = FIRST; type <= LAST; type = type << 1) {
+ final int id = InsetsSource.createId(owner, index, type);
+ final int typeFromId = InsetsSource.getType(id);
+ assertEquals("type and typeFromId must be the same. id=" + id
+ + ", owner=" + owner
+ + ", index=" + index
+ + ", type=" + type
+ + ", typeFromId=" + typeFromId + ".", type, typeFromId);
+ }
}
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index c6e13a1..aa5ce30 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -20,12 +20,21 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalConfiguration
import com.android.settingslib.spa.framework.theme.SettingsDimension
import kotlin.math.absoluteValue
import kotlinx.coroutines.launch
@@ -41,7 +50,7 @@
Column {
val coroutineScope = rememberCoroutineScope()
- val pagerState = rememberPagerState()
+ val pagerState by rememberPageStateFixed()
TabRow(
selectedTabIndex = pagerState.currentPage,
@@ -69,3 +78,28 @@
}
}
}
+
+/**
+ * Gets the state of [PagerState].
+ *
+ * This is a work around.
+ *
+ * TODO: Remove this and replace with rememberPageState() after the Compose Foundation 1.5.0-alpha04
+ * updated in the platform.
+ */
+@Composable
+@OptIn(ExperimentalFoundationApi::class)
+private fun rememberPageStateFixed(): State<PagerState> {
+ var currentPage by rememberSaveable { mutableStateOf(0) }
+ val pagerStateHolder = remember { mutableStateOf(PagerState(currentPage)) }
+ LaunchedEffect(LocalConfiguration.current.orientation) {
+ // Reset pager state to fix an issue after configuration change.
+ // When we declare android:configChanges="orientation" in the manifest, the pager state is
+ // in a weird state after configuration change.
+ pagerStateHolder.value = PagerState(currentPage)
+ }
+ SideEffect {
+ currentPage = pagerStateHolder.value.currentPage
+ }
+ return pagerStateHolder
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index bd7898a..c582cfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -25,8 +25,11 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.Intent.ACTION_CREATE_NOTE
import android.content.Intent.ACTION_MAIN
+import android.content.Intent.ACTION_MANAGE_DEFAULT_APP
import android.content.Intent.CATEGORY_HOME
+import android.content.Intent.EXTRA_USE_STYLUS_MODE
import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
@@ -47,8 +50,10 @@
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
+import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
@@ -221,31 +226,22 @@
// region showNoteTask
@Test
fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() {
- val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isKeyguardLocked = true,
- )
+ val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
- createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- )
+ createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
+ assertThat(intentCaptor.value).run {
+ hasAction(ACTION_CREATE_NOTE)
+ hasPackage(NOTE_TASK_PACKAGE_NAME)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
+ hasFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
+ hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
@@ -256,32 +252,23 @@
fun showNoteTaskWithUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() {
val user10 = UserHandle.of(/* userId= */ 10)
val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isKeyguardLocked = true,
- user = user10,
- )
+ NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
- .showNoteTaskAsUser(
- entryPoint = expectedInfo.entryPoint!!,
- user = user10,
- )
+ .showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
+ assertThat(intentCaptor.value).run {
+ hasAction(ACTION_CREATE_NOTE)
+ hasPackage(NOTE_TASK_PACKAGE_NAME)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
+ hasFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
+ hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
assertThat(userCaptor.value).isEqualTo(user10)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
@@ -290,11 +277,7 @@
@Test
fun showNoteTask_keyguardIsLocked_noteIsOpen_shouldCloseActivityAndLogUiEvent() {
- val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isKeyguardLocked = true,
- )
+ val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(activityManager.getRunningTasks(anyInt()))
@@ -305,10 +288,10 @@
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(ACTION_MAIN)
- assertThat(intent.categories).contains(CATEGORY_HOME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+ assertThat(intentCaptor.value).run {
+ hasAction(ACTION_MAIN)
+ categories().contains(CATEGORY_HOME)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskClosed(expectedInfo)
@@ -317,18 +300,11 @@
@Test
fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() {
- val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- isKeyguardLocked = false,
- )
+ val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- )
+ createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
// Context package name used to create bubble icon from drawable resource id
verify(context).packageName
@@ -338,10 +314,7 @@
@Test
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
- createNoteTaskController(bubbles = null)
- .showNoteTask(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- )
+ createNoteTaskController(bubbles = null).showNoteTask(entryPoint = TAIL_BUTTON)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -352,7 +325,7 @@
val noteTaskController = spy(createNoteTaskController())
doNothing().whenever(noteTaskController).showNoDefaultNotesAppToast()
- noteTaskController.showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
+ noteTaskController.showNoteTask(entryPoint = TAIL_BUTTON)
verify(noteTaskController).showNoDefaultNotesAppToast()
verifyZeroInteractions(context, bubbles, eventLogger)
@@ -360,10 +333,7 @@
@Test
fun showNoteTask_flagDisabled_shouldDoNothing() {
- createNoteTaskController(isEnabled = false)
- .showNoteTask(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- )
+ createNoteTaskController(isEnabled = false).showNoteTask(entryPoint = TAIL_BUTTON)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -372,10 +342,7 @@
fun showNoteTask_userIsLocked_shouldDoNothing() {
whenever(userManager.isUserUnlocked).thenReturn(false)
- createNoteTaskController()
- .showNoteTask(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- )
+ createNoteTaskController().showNoteTask(entryPoint = TAIL_BUTTON)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -383,30 +350,22 @@
@Test
fun showNoteTask_keyboardShortcut_shouldStartActivity() {
val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
- isKeyguardLocked = true,
- )
+ NOTE_TASK_INFO.copy(entryPoint = KEYBOARD_SHORTCUT, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
- createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- )
+ createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
- assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
+ assertThat(intentCaptor.value).run {
+ hasAction(ACTION_CREATE_NOTE)
+ hasPackage(NOTE_TASK_PACKAGE_NAME)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
+ hasFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
+ hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ extras().bool(EXTRA_USE_STYLUS_MODE).isFalse()
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
@@ -583,7 +542,7 @@
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
+ createNoteTaskController().showNoteTask(entryPoint = TAIL_BUTTON)
verifyNoteTaskOpenInBubbleInUser(workUserInfo.userHandle)
}
@@ -593,8 +552,7 @@
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController()
- .showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)
+ createNoteTaskController().showNoteTask(entryPoint = WIDGET_PICKER_SHORTCUT)
verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle)
}
@@ -604,7 +562,7 @@
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.APP_CLIPS)
+ createNoteTaskController().showNoteTask(entryPoint = APP_CLIPS)
verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle)
}
@@ -615,13 +573,13 @@
val iconCaptor = argumentCaptor<Icon>()
verify(bubbles)
.showOrHideAppBubble(capture(intentCaptor), eq(userHandle), capture(iconCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
- assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
- assertThat(intent.flags).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
- assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue()
+ assertThat(intentCaptor.value).run {
+ hasAction(ACTION_CREATE_NOTE)
+ hasPackage(NOTE_TASK_PACKAGE_NAME)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
+ extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
- iconCaptor.value.let { icon ->
+ iconCaptor.value?.let { icon ->
assertThat(icon).isNotNull()
assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
}
@@ -679,9 +637,10 @@
verify(shortcutManager).updateShortcuts(actualShortcuts.capture())
val actualShortcut = actualShortcuts.value.first()
assertThat(actualShortcut.id).isEqualTo(SHORTCUT_ID)
- assertThat(actualShortcut.intent?.component?.className)
- .isEqualTo(LaunchNoteTaskActivity::class.java.name)
- assertThat(actualShortcut.intent?.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
+ assertThat(actualShortcut.intent).run {
+ hasComponentClass(LaunchNoteTaskActivity::class.java)
+ hasAction(ACTION_CREATE_NOTE)
+ }
assertThat(actualShortcut.shortLabel).isEqualTo(NOTE_TASK_SHORT_LABEL)
assertThat(actualShortcut.isLongLived).isEqualTo(true)
assertThat(actualShortcut.icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
@@ -737,12 +696,9 @@
val intentCaptor = argumentCaptor<Intent>()
verify(context).startActivityAsUser(intentCaptor.capture(), eq(user0))
- intentCaptor.value.let { intent ->
- assertThat(intent)
- .hasComponent(
- ComponentName(context, LaunchNoteTaskManagedProfileProxyActivity::class.java)
- )
- assertThat(intent).hasFlags(FLAG_ACTIVITY_NEW_TASK)
+ assertThat(intentCaptor.value).run {
+ hasComponentClass(LaunchNoteTaskManagedProfileProxyActivity::class.java)
+ hasFlags(FLAG_ACTIVITY_NEW_TASK)
}
}
// endregion
@@ -817,9 +773,7 @@
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
- }
+ assertThat(intentCaptor.value).hasAction(ACTION_MANAGE_DEFAULT_APP)
assertThat(userCaptor.value).isEqualTo(UserHandle.of(workUserInfo.id))
}
@@ -833,9 +787,7 @@
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
- }
+ assertThat(intentCaptor.value).hasAction(ACTION_MANAGE_DEFAULT_APP)
assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
}
@@ -848,9 +800,7 @@
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
- }
+ assertThat(intentCaptor.value).hasAction(ACTION_MANAGE_DEFAULT_APP)
assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
}
@@ -863,9 +813,7 @@
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
- intentCaptor.value.let { intent ->
- assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
- }
+ assertThat(intentCaptor.value).hasAction(ACTION_MANAGE_DEFAULT_APP)
assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
}
// endregion
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c393213..fbe7e70 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -107,6 +107,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManagerInternal;
+import android.provider.DeviceConfig;
import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
@@ -181,6 +182,8 @@
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
+ private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
+
// OOM adjustments for processes in various states:
// Uninitialized value for any major or minor adj fields
@@ -538,6 +541,78 @@
ActivityManagerGlobalLock mProcLock;
+ private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
+ "apply_sdk_sandbox_next_restrictions";
+ private static final boolean DEFAULT_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = false;
+
+ @GuardedBy("mService")
+ private ProcessListSettingsListener mProcessListSettingsListener;
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ ProcessListSettingsListener getProcessListSettingsListener() {
+ synchronized (mService) {
+ if (mProcessListSettingsListener == null) {
+ mProcessListSettingsListener = new ProcessListSettingsListener(mService.mContext);
+ mProcessListSettingsListener.registerObserver();
+ }
+ return mProcessListSettingsListener;
+ }
+ }
+
+ static class ProcessListSettingsListener implements DeviceConfig.OnPropertiesChangedListener {
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mSdkSandboxApplyRestrictionsNext =
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS,
+ DEFAULT_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
+
+ ProcessListSettingsListener(Context context) {
+ mContext = context;
+ }
+
+ private void registerObserver() {
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_ADSERVICES, mContext.getMainExecutor(), this);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ void unregisterObserver() {
+ DeviceConfig.removeOnPropertiesChangedListener(this);
+ }
+
+ boolean applySdkSandboxRestrictionsNext() {
+ synchronized (mLock) {
+ return mSdkSandboxApplyRestrictionsNext;
+ }
+ }
+
+ @Override
+ public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ synchronized (mLock) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+
+ switch (name) {
+ case PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS:
+ mSdkSandboxApplyRestrictionsNext =
+ properties.getBoolean(
+ PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS,
+ DEFAULT_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
+ break;
+ default:
+ }
+ }
+ }
+ }
+ }
+
final class IsolatedUidRange {
@VisibleForTesting
public final int mFirstUid;
@@ -1883,8 +1958,9 @@
new IllegalStateException("SELinux tag not defined for "
+ app.info.packageName + " (uid " + app.uid + ")"));
}
- final String seInfo = app.info.seInfo
- + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
+
+ String seInfo = updateSeInfo(app);
+
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
final String entryPoint = "android.app.ActivityThread";
@@ -1907,6 +1983,21 @@
}
}
+ @VisibleForTesting
+ @GuardedBy("mService")
+ String updateSeInfo(ProcessRecord app) {
+ String extraInfo = "";
+ // By the time the first the SDK sandbox process is started, device config service
+ // should be available.
+ if (app.isSdkSandbox
+ && getProcessListSettingsListener().applySdkSandboxRestrictionsNext()) {
+ extraInfo = APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS;
+ }
+
+ return app.info.seInfo
+ + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser) + extraInfo;
+ }
+
@GuardedBy("mService")
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1e50f3d..d93778e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -78,7 +78,6 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
@@ -9323,8 +9322,7 @@
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
}
-
- if (!info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO)) {
+ if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 2b72215..1fbf593 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -245,7 +245,7 @@
}
/**
- * Notifies that animation in {@link ScreenAnimationRotation} has finished.
+ * Notifies that animation in {@link ScreenRotationAnimation} has finished.
*
* <p>This class uses this signal as a trigger for notifying the user about forced rotation
* reason with the {@link Toast}.
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 5b39790..b557b2e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -25,6 +25,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
@@ -49,6 +50,7 @@
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
@@ -185,10 +187,15 @@
// when dealing with translucent activities.
private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>();
+ // Corresponds to OVERRIDE_MIN_ASPECT_RATIO
+ private final boolean mIsOverrideMinAspectRatio;
+
@Nullable
private final Boolean mBooleanPropertyAllowOrientationOverride;
@Nullable
private final Boolean mBooleanPropertyAllowDisplayOrientationOverride;
+ @Nullable
+ private final Boolean mBooleanPropertyAllowMinAspectRatioOverride;
/*
* WindowContainerListener responsible to make translucent activities inherit
@@ -300,6 +307,10 @@
readComponentProperty(packageManager, mActivityRecord.packageName,
/* gatingCondition */ null,
PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE);
+ mBooleanPropertyAllowMinAspectRatioOverride =
+ readComponentProperty(packageManager, mActivityRecord.packageName,
+ /* gatingCondition */ null,
+ PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
mIsOverrideToPortraitOrientationEnabled =
@@ -330,6 +341,7 @@
mIsOverrideEnableCompatFakeFocusEnabled =
isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS);
+ mIsOverrideMinAspectRatio = isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO);
}
/**
@@ -502,6 +514,25 @@
}
/**
+ * Whether we should apply the min aspect ratio per-app override. When this override is applied
+ * the min aspect ratio given in the app's manifest will be overridden to the largest enabled
+ * aspect ratio treatment unless the app's manifest value is higher. The treatment will also
+ * apply if no value is provided in the manifest.
+ *
+ * <p>This method returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * </ul>
+ */
+ boolean shouldOverrideMinAspectRatio() {
+ return shouldEnableWithOptInOverrideAndOptOutProperty(
+ /* gatingCondition */ () -> true,
+ mIsOverrideMinAspectRatio,
+ mBooleanPropertyAllowMinAspectRatioOverride);
+ }
+
+ /**
* Sets whether an activity is relaunching after the app has called {@link
* android.app.Activity#setRequestedOrientation}.
*/
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5e60e921..e68bb71 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1194,6 +1194,13 @@
if (asyncRotationController != null && containsChangeFor(dc, mTargets)) {
asyncRotationController.onTransitionFinished();
}
+ if (hasParticipatedDisplay && dc.mDisplayRotationCompatPolicy != null) {
+ final ChangeInfo changeInfo = mChanges.get(dc);
+ if (changeInfo != null
+ && changeInfo.mRotation != dc.getWindowConfiguration().getRotation()) {
+ dc.mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+ }
+ }
if (mTransientLaunches != null) {
InsetsControlTarget prevImeTarget = dc.getImeTarget(
DisplayContent.IME_TARGET_CONTROL);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9d1c77d..324e260 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15135,7 +15135,7 @@
return;
}
- if ((flags == 0) || ((flags & ~(allowedFlags)) != 0)) {
+ if ((flags & ~(allowedFlags)) != 0) {
throw new SecurityException(
"Permitted lock task features when managing a financed device: "
+ "LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_KEYGUARD, "
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index bfe7d7f..a614c4d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -37,6 +37,7 @@
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
@@ -60,6 +61,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -82,13 +84,16 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.StickyBroadcast;
import com.android.server.am.ProcessList.IsolatedUidRange;
@@ -106,6 +111,7 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.io.File;
import java.util.ArrayList;
@@ -137,7 +143,9 @@
private static final String TEST_EXTRA_KEY1 = "com.android.server.am.TEST_EXTRA_KEY1";
private static final String TEST_EXTRA_VALUE1 = "com.android.server.am.TEST_EXTRA_VALUE1";
-
+ private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
+ "apply_sdk_sandbox_next_restrictions";
+ private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
private static final int TEST_UID = 11111;
private static final int USER_ID = 666;
@@ -154,6 +162,7 @@
};
private static PackageManagerInternal sPackageManagerInternal;
+ private static ProcessList.ProcessListSettingsListener sProcessListSettingsListener;
@BeforeClass
public static void setUpOnce() {
@@ -181,7 +190,6 @@
private ActivityManagerService mAms;
private HandlerThread mHandlerThread;
private TestHandler mHandler;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -203,6 +211,15 @@
mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
mHandler.setRunnablesToIgnore(
List.of(mAms.mUidObserverController.getDispatchRunnableForTest()));
+
+ // Required for updating DeviceConfig.
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(
+ Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
+ sProcessListSettingsListener = mAms.mProcessList.getProcessListSettingsListener();
+ assertThat(sProcessListSettingsListener).isNotNull();
}
private void mockNoteOperation() {
@@ -216,6 +233,12 @@
@After
public void tearDown() {
mHandlerThread.quit();
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ if (sProcessListSettingsListener != null) {
+ sProcessListSettingsListener.unregisterObserver();
+ }
}
@SuppressWarnings("GuardedBy")
@@ -268,6 +291,77 @@
false); // expectNotify
}
+ @SuppressWarnings("GuardedBy")
+ @SmallTest
+ @Test
+ public void defaultSdkSandboxNextRestrictions() throws Exception {
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "")));
+ assertThat(
+ sProcessListSettingsListener.applySdkSandboxRestrictionsNext())
+ .isFalse();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @SmallTest
+ @Test
+ public void doNotApplySdkSandboxNextRestrictions() throws Exception {
+ MockitoSession mockitoSession =
+ ExtendedMockito.mockitoSession().spyStatic(Process.class).startMocking();
+ try {
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "false")));
+ assertThat(
+ sProcessListSettingsListener.applySdkSandboxRestrictionsNext())
+ .isFalse();
+ ExtendedMockito.doReturn(true).when(() -> Process.isSdkSandboxUid(anyInt()));
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = "com.android.sdksandbox";
+ info.seInfo = "default:targetSdkVersion=34:complete";
+ final ProcessRecord appRec = new ProcessRecord(
+ mAms, info, TAG, Process.FIRST_SDK_SANDBOX_UID,
+ /* sdkSandboxClientPackageName= */ "com.example.client",
+ /* definingUid= */ 0, /* definingProcessName= */ "");
+ assertThat(mAms.mProcessList.updateSeInfo(appRec)).doesNotContain(
+ APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+ @SuppressWarnings("GuardedBy")
+ @SmallTest
+ @Test
+ public void applySdkSandboxNextRestrictions() throws Exception {
+ MockitoSession mockitoSession =
+ ExtendedMockito.mockitoSession().spyStatic(Process.class).startMocking();
+ try {
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true")));
+ assertThat(
+ sProcessListSettingsListener.applySdkSandboxRestrictionsNext())
+ .isTrue();
+ ExtendedMockito.doReturn(true).when(() -> Process.isSdkSandboxUid(anyInt()));
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = "com.android.sdksandbox";
+ info.seInfo = "default:targetSdkVersion=34:complete";
+ final ProcessRecord appRec = new ProcessRecord(
+ mAms, info, TAG, Process.FIRST_SDK_SANDBOX_UID,
+ /* sdkSandboxClientPackageName= */ "com.example.client",
+ /* definingUid= */ 0, /* definingProcessName= */ "");
+ assertThat(mAms.mProcessList.updateSeInfo(appRec)).contains(
+ APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
+
private UidRecord addUidRecord(int uid) {
final UidRecord uidRec = new UidRecord(uid, mAms);
uidRec.procStateSeqWaitingForNetwork = 1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 03c93e9..81e0fb3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -24,6 +24,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
@@ -40,6 +41,7 @@
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
@@ -861,6 +863,62 @@
}
@Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertTrue(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertTrue(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldOverrideMinAspectRatio());
+ }
+
+ @Test
public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
.isCameraCompatTreatmentEnabled(anyBoolean());