summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/supervision/ISupervisionManager.aidl1
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java17
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java38
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java6
-rw-r--r--packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt20
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginManager.java10
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginStorage.java200
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionStopController.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java21
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java96
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java12
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionUserData.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt75
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt50
27 files changed, 484 insertions, 335 deletions
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 4598421eb3bc..c3f3b1ced33c 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -22,4 +22,5 @@ package android.app.supervision;
*/
interface ISupervisionManager {
boolean isSupervisionEnabledForUser(int userId);
+ String getActiveSupervisionAppPackage(int userId);
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 92241f3634e8..12432ddd0eb9 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,6 +16,7 @@
package android.app.supervision;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
@@ -98,4 +99,20 @@ public class SupervisionManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the package name of the app that is acting as the active supervision app or null if
+ * supervision is disabled.
+ *
+ * @hide
+ */
+ @UserHandleAware
+ @Nullable
+ public String getActiveSupervisionAppPackage() {
+ try {
+ return mService.getActiveSupervisionAppPackage(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 6da2a073ec19..1cf42820f356 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -18,13 +18,6 @@ flag {
}
flag {
- namespace: "virtual_devices"
- name: "media_projection_keyguard_restrictions"
- description: "Auto-stop MP when the device locks"
- bug: "348335290"
-}
-
-flag {
namespace: "virtual_devices"
name: "virtual_display_insets"
description: "APIs for specifying virtual display insets (via cutout)"
diff --git a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
index de93234445ca..d3fb93588762 100644
--- a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
+++ b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.supervision.SupervisionManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
@@ -55,27 +56,44 @@ public class ParentalControlsUtilsInternal {
return null;
}
- public static boolean parentConsentRequired(@NonNull Context context,
- @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality,
+ /** @return true if parental consent is required in order for biometric sensors to be used. */
+ public static boolean parentConsentRequired(
+ @NonNull Context context,
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
@NonNull UserHandle userHandle) {
if (getTestComponentName(context, userHandle.getIdentifier()) != null) {
return true;
}
- return parentConsentRequired(dpm, modality, userHandle);
+ return parentConsentRequired(dpm, sm, modality, userHandle);
}
/**
* @return true if parental consent is required in order for biometric sensors to be used.
*/
- public static boolean parentConsentRequired(@NonNull DevicePolicyManager dpm,
- @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) {
- final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
- if (cn == null) {
- return false;
+ public static boolean parentConsentRequired(
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
+ @NonNull UserHandle userHandle) {
+ final int keyguardDisabledFeatures;
+
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ if (sm != null && !sm.isSupervisionEnabledForUser(userHandle.getIdentifier())) {
+ return false;
+ }
+ // Check for keyguard features disabled by any admin.
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(/* admin= */ null);
+ } else {
+ final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
+ if (cn == null) {
+ return false;
+ }
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
}
- final int keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
final boolean dpmFpDisabled = containsFlag(keyguardDisabledFeatures,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
final boolean dpmFaceDisabled = containsFlag(keyguardDisabledFeatures,
@@ -97,7 +115,9 @@ public class ParentalControlsUtilsInternal {
return consentRequired;
}
+ /** @deprecated Use {@link SupervisionManager} to check for supervision. */
@Nullable
+ @Deprecated
public static ComponentName getSupervisionComponentName(@NonNull DevicePolicyManager dpm,
@NonNull UserHandle userHandle) {
return dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e35c3b80a58b..222088e8a8b9 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -583,3 +583,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_start_launch_transition_from_taskbar_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables starting a launch transition directly from the taskbar if desktop tasks are visible."
+ bug: "361366053"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index e3d7902f34b2..00973811dbf0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -84,7 +84,11 @@ public final class ActionDisabledByAdminControllerFactory {
return false;
}
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
- return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ final SupervisionManager sm =
+ android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()
+ ? context.getSystemService(SupervisionManager.class)
+ : null;
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm, sm,
BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
diff --git a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
index f7dac13888c0..ea494b4642d5 100644
--- a/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/bouncer_message_view.xml
@@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyguard_lock_padding"
- android:focusable="true"
+ android:focusable="false"
/>
<com.android.keyguard.BouncerKeyguardMessageArea
@@ -30,6 +30,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/secondary_message_padding"
- android:focusable="true" />
+ android:focusable="false" />
</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
index 2a8f1b596711..f231df2f1a10 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_motion_layout.xml
@@ -66,7 +66,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 76f6f599c54c..04457229d573 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -31,7 +31,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
index 5879c110d8a1..b184344f2f24 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_motion_layout.xml
@@ -67,7 +67,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 3f7b02835357..0e15ff66f3ee 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -35,7 +35,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
index b464fb3bafed..f6ac02aee657 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_motion_layout.xml
@@ -74,7 +74,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 21580731aed2..ba4da794d777 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -32,7 +32,7 @@
<com.android.systemui.bouncer.ui.BouncerMessageView
android:id="@+id/bouncer_message_view"
- android:importantForAccessibility="noHideDescendants"
+ android:screenReaderFocusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 7f176de547bc..0e9d8fec9717 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -164,6 +166,8 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
layoutParams.height = (int) getResources().getDimension(
R.dimen.keyguard_pin_field_height);
}
+
+ mPasswordEntry.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
}
private void setKeyboardBasedFocusOutline(boolean isAnyKeyboardConnected) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 69b7e892a380..9795cda97f37 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -49,8 +49,10 @@ import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
@@ -80,6 +82,7 @@ fun TutorialSelectionScreen(
}
),
) {
+ val padding = if (hasCompactWindowSize()) 24.dp else 60.dp
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
@@ -88,7 +91,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -98,7 +101,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -106,7 +109,7 @@ fun TutorialSelectionScreen(
// because other composables have weight 1, Done button will be positioned first
DoneButton(
onDoneButtonClicked = onDoneButtonClicked,
- modifier = Modifier.padding(horizontal = 60.dp),
+ modifier = Modifier.padding(horizontal = padding),
)
}
}
@@ -146,7 +149,7 @@ private fun VerticalSelectionButtons(
lastSelectedScreen: Screen,
) {
Column(
- verticalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
@@ -244,8 +247,13 @@ private fun TutorialButton(
modifier = Modifier.width(30.dp).height(30.dp),
tint = iconColor,
)
- Spacer(modifier = Modifier.height(16.dp))
- Text(text = text, style = MaterialTheme.typography.headlineLarge, color = iconColor)
+ if (!hasCompactWindowSize()) Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.headlineLarge,
+ color = iconColor,
+ )
}
}
}
diff --git a/services/core/java/com/android/server/display/plugin/PluginManager.java b/services/core/java/com/android/server/display/plugin/PluginManager.java
index d4099975cafa..cb0a4574361a 100644
--- a/services/core/java/com/android/server/display/plugin/PluginManager.java
+++ b/services/core/java/com/android/server/display/plugin/PluginManager.java
@@ -74,15 +74,17 @@ public class PluginManager {
/**
* Adds change listener for particular plugin type
*/
- public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.addListener(type, listener);
+ public <T> void subscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.addListener(type, uniqueDisplayId, listener);
}
/**
* Removes change listener
*/
- public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.removeListener(type, listener);
+ public <T> void unsubscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.removeListener(type, uniqueDisplayId, listener);
}
/**
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index dd3415fb614d..5102c2709329 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -20,10 +20,13 @@ import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -35,42 +38,97 @@ import java.util.Set;
public class PluginStorage {
private static final String TAG = "PluginStorage";
+ // Special ID used to indicate that given value is to be applied globally, rather than to a
+ // specific display. If both GLOBAL and specific display values are present - specific display
+ // value is selected.
+ @VisibleForTesting
+ static final String GLOBAL_ID = "GLOBAL";
+
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final Map<PluginType<?>, Object> mValues = new HashMap<>();
+ private final Map<PluginType<?>, ValuesContainer<?>> mValues = new HashMap<>();
@GuardedBy("mLock")
private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
@GuardedBy("mLock")
- private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
+ private final Map<String, PluginEventStorage> mPluginEventStorages = new HashMap<>();
+
+ /**
+ * Updates value in storage and forwards it to corresponding listeners for all displays
+ * that does not have display specific value.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ */
+ @KeepForApi
+ public <T> void updateGlobalValue(PluginType<T> type, @Nullable T value) {
+ updateValue(type, GLOBAL_ID, value);
+ }
/**
- * Updates value in storage and forwards it to corresponding listeners.
- * Should be called by OEM Plugin implementation in order to provide communicate with Framework
+ * Updates value in storage and forwards it to corresponding listeners for specific display.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ * @param type - plugin type, that need to be updated
+ * @param uniqueDisplayId - uniqueDisplayId that this type/value should be applied to
+ * @param value - plugin value for particular type and display
*/
@KeepForApi
- public <T> void updateValue(PluginType<T> type, @Nullable T value) {
- Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value);
+ public <T> void updateValue(PluginType<T> type, String uniqueDisplayId, @Nullable T value) {
+ Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value
+ + "; displayId=" + uniqueDisplayId);
Set<PluginManager.PluginChangeListener<T>> localListeners;
+ T valueToNotify;
synchronized (mLock) {
- mValues.put(type, value);
- mPluginEventStorage.onValueUpdated(type);
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- localListeners = new LinkedHashSet<>(container.mListeners);
+ ValuesContainer<T> valuesByType = getValuesContainerLocked(type);
+ valuesByType.updateValueLocked(uniqueDisplayId, value);
+ // if value was set to null, we might need to notify with GLOBAL value instead
+ valueToNotify = valuesByType.getValueLocked(uniqueDisplayId);
+
+ PluginEventStorage storage = mPluginEventStorages.computeIfAbsent(uniqueDisplayId,
+ d -> new PluginEventStorage());
+ storage.onValueUpdated(type);
+
+ localListeners = getListenersForUpdateLocked(type, uniqueDisplayId);
}
Slog.d(TAG, "updateValue, notifying listeners=" + localListeners);
- localListeners.forEach(l -> l.onChanged(value));
+ localListeners.forEach(l -> l.onChanged(valueToNotify));
+ }
+
+ @GuardedBy("mLock")
+ private <T> Set<PluginManager.PluginChangeListener<T>> getListenersForUpdateLocked(
+ PluginType<T> type, String uniqueDisplayId) {
+ ListenersContainer<T> listenersContainer = getListenersContainerLocked(type);
+ Set<PluginManager.PluginChangeListener<T>> localListeners = new LinkedHashSet<>();
+ // if GLOBAL value change we need to notify only listeners for displays that does not
+ // have display specific value
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ Set<String> excludedDisplayIds = valuesContainer.getNonGlobalDisplaysLocked();
+ listenersContainer.mListeners.forEach((localDisplayId, listeners) -> {
+ if (!excludedDisplayIds.contains(localDisplayId)) {
+ localListeners.addAll(listeners);
+ }
+ });
+ } else {
+ localListeners.addAll(
+ listenersContainer.mListeners.getOrDefault(uniqueDisplayId, Set.of()));
+ }
+ return localListeners;
}
/**
* Adds listener for PluginType. If storage already has value for this type, listener will
* be notified immediately.
*/
- <T> void addListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void addListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "addListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
T value = null;
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- if (container.mListeners.add(listener)) {
- value = getValueForTypeLocked(type);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ if (container.addListenerLocked(uniqueDisplayId, listener)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ value = valuesContainer.getValueLocked(uniqueDisplayId);
}
}
if (value != null) {
@@ -81,10 +139,15 @@ public class PluginStorage {
/**
* Removes listener
*/
- <T> void removeListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void removeListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "removeListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- container.mListeners.remove(listener);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ container.removeListenerLocked(uniqueDisplayId, listener);
}
}
@@ -92,53 +155,106 @@ public class PluginStorage {
* Print the object's state and debug information into the given stream.
*/
void dump(PrintWriter pw) {
- Map<PluginType<?>, Object> localValues;
+ Map<PluginType<?>, Map<String, Object>> localValues = new HashMap<>();
@SuppressWarnings("rawtypes")
- Map<PluginType, Set> localListeners = new HashMap<>();
- List<PluginEventStorage.TimeFrame> timeFrames;
+ Map<PluginType, Map<String, Set>> localListeners = new HashMap<>();
+ Map<String, List<PluginEventStorage.TimeFrame>> timeFrames = new HashMap<>();
synchronized (mLock) {
- timeFrames = mPluginEventStorage.getTimeFrames();
- localValues = new HashMap<>(mValues);
- mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
+ mPluginEventStorages.forEach((displayId, storage) -> {
+ timeFrames.put(displayId, storage.getTimeFrames());
+ });
+ mValues.forEach((type, valueContainer) -> {
+ localValues.put(type, new HashMap<>(valueContainer.mValues));
+ });
+ mListeners.forEach((type, container) -> {
+ localListeners.put(type, new HashMap<>(container.mListeners));
+ });
}
pw.println("PluginStorage:");
pw.println("values=" + localValues);
pw.println("listeners=" + localListeners);
pw.println("PluginEventStorage:");
- for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
- timeFrame.dump(pw);
+ for (Map.Entry<String, List<PluginEventStorage.TimeFrame>> timeFrameEntry :
+ timeFrames.entrySet()) {
+ pw.println("TimeFrames for displayId=" + timeFrameEntry.getKey());
+ for (PluginEventStorage.TimeFrame timeFrame : timeFrameEntry.getValue()) {
+ timeFrame.dump(pw);
+ }
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> T getValueForTypeLocked(PluginType<T> type) {
- Object value = mValues.get(type);
- if (value == null) {
- return null;
- } else if (type.mType == value.getClass()) {
- return (T) value;
+ private <T> ListenersContainer<T> getListenersContainerLocked(PluginType<T> type) {
+ ListenersContainer<?> container = mListeners.get(type);
+ if (container == null) {
+ ListenersContainer<T> lc = new ListenersContainer<>();
+ mListeners.put(type, lc);
+ return lc;
} else {
- Slog.d(TAG, "getValueForType: unexpected value type=" + value.getClass().getName()
- + ", expected=" + type.mType.getName());
- return null;
+ return (ListenersContainer<T>) container;
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> ListenersContainer<T> getListenersContainerForTypeLocked(PluginType<T> type) {
- ListenersContainer<?> container = mListeners.get(type);
+ private <T> ValuesContainer<T> getValuesContainerLocked(PluginType<T> type) {
+ ValuesContainer<?> container = mValues.get(type);
if (container == null) {
- ListenersContainer<T> lc = new ListenersContainer<>();
- mListeners.put(type, lc);
- return lc;
+ ValuesContainer<T> vc = new ValuesContainer<>();
+ mValues.put(type, vc);
+ return vc;
} else {
- return (ListenersContainer<T>) container;
+ return (ValuesContainer<T>) container;
}
}
private static final class ListenersContainer<T> {
- private final Set<PluginManager.PluginChangeListener<T>> mListeners = new LinkedHashSet<>();
+ private final Map<String, Set<PluginManager.PluginChangeListener<T>>> mListeners =
+ new LinkedHashMap<>();
+
+ private boolean addListenerLocked(
+ String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay =
+ mListeners.computeIfAbsent(uniqueDisplayId, k -> new LinkedHashSet<>());
+ return listenersForDisplay.add(listener);
+ }
+
+ private void removeListenerLocked(String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay = mListeners.get(
+ uniqueDisplayId);
+ if (listenersForDisplay == null) {
+ return;
+ }
+
+ listenersForDisplay.remove(listener);
+
+ if (listenersForDisplay.isEmpty()) {
+ mListeners.remove(uniqueDisplayId);
+ }
+ }
+ }
+
+ private static final class ValuesContainer<T> {
+ private final Map<String, T> mValues = new HashMap<>();
+
+ private void updateValueLocked(String uniqueDisplayId, @Nullable T value) {
+ if (value == null) {
+ mValues.remove(uniqueDisplayId);
+ } else {
+ mValues.put(uniqueDisplayId, value);
+ }
+ }
+
+ private Set<String> getNonGlobalDisplaysLocked() {
+ Set<String> keys = new HashSet<>(mValues.keySet());
+ keys.remove(GLOBAL_ID);
+ return keys;
+ }
+
+ private @Nullable T getValueLocked(String displayId) {
+ return mValues.getOrDefault(displayId, mValues.get(GLOBAL_ID));
+ }
}
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index 2e0bb4f88485..18f2f48b80a3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyCallback;
@@ -38,6 +39,7 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -60,21 +62,35 @@ public class MediaProjectionStopController {
private final TelephonyManager mTelephonyManager;
private final AppOpsManager mAppOpsManager;
private final PackageManager mPackageManager;
- private final RoleManager mRoleManager;
+ private final RoleHolderProvider mRoleHolderProvider;
private final ContentResolver mContentResolver;
private boolean mIsInCall;
private long mLastCallStartTimeMillis;
+
+ @VisibleForTesting
+ interface RoleHolderProvider {
+ List<String> getRoleHoldersAsUser(String roleName, UserHandle user);
+ }
+
public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
+ this(context, stopReasonConsumer,
+ (roleName, user) -> context.getSystemService(RoleManager.class)
+ .getRoleHoldersAsUser(roleName, user));
+ }
+
+ @VisibleForTesting
+ MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer,
+ RoleHolderProvider roleHolderProvider) {
mStopReasonConsumer = stopReasonConsumer;
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mTelecomManager = context.getSystemService(TelecomManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mPackageManager = context.getPackageManager();
- mRoleManager = context.getSystemService(RoleManager.class);
mContentResolver = context.getContentResolver();
+ mRoleHolderProvider = roleHolderProvider;
}
/**
@@ -146,8 +162,9 @@ public class MediaProjectionStopController {
Slog.v(TAG, "Continuing MediaProjection for package with OP_PROJECT_MEDIA AppOp ");
return true;
}
- if (mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- projectionGrant.userHandle).contains(projectionGrant.packageName)) {
+ if (mRoleHolderProvider.getRoleHoldersAsUser(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING, projectionGrant.userHandle)
+ .contains(projectionGrant.packageName)) {
Slog.v(TAG, "Continuing MediaProjection for package holding app streaming role.");
return true;
}
@@ -177,10 +194,6 @@ public class MediaProjectionStopController {
*/
public boolean isStartForbidden(
MediaProjectionManagerService.MediaProjection projectionGrant) {
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return false;
- }
-
if (!mKeyguardManager.isKeyguardLocked()) {
return false;
}
@@ -194,9 +207,6 @@ public class MediaProjectionStopController {
@VisibleForTesting
void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
if (!isKeyguardLocked) return;
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return;
- }
mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e761e024b3ca..883d8f95b612 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1680,26 +1680,27 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Sets the specified orientation of this container. It percolates this change upward along the
* hierarchy to let each level of the hierarchy a chance to respond to it.
*
- * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
+ * @param requestedOrientation the specified orientation. Needs to be one of
+ * {@link ScreenOrientation}.
* @param requestingContainer the container which orientation request has changed. Mostly used
* to ensure it gets correct configuration.
* @return the resolved override orientation of this window container.
*/
@ScreenOrientation
- int setOrientation(@ScreenOrientation int orientation,
+ int setOrientation(@ScreenOrientation int requestedOrientation,
@Nullable WindowContainer requestingContainer) {
- if (getOverrideOrientation() == orientation) {
- return orientation;
+ if (getOverrideOrientation() == requestedOrientation) {
+ return requestedOrientation;
}
- setOverrideOrientation(orientation);
+ setOverrideOrientation(requestedOrientation);
final WindowContainer parent = getParent();
if (parent == null) {
- return orientation;
+ return requestedOrientation;
}
// The derived class can return a result that is different from the given orientation.
- final int resolvedOrientation = getOverrideOrientation();
+ final int actualOverrideOrientation = getOverrideOrientation();
if (getConfiguration().orientation != getRequestedConfigurationOrientation(
- false /* forDisplay */, resolvedOrientation)
+ false /* forDisplay */, actualOverrideOrientation)
// Update configuration directly only if the change won't be dispatched from
// ancestor. This prevents from computing intermediate configuration when the
// parent also needs to be updated from the ancestor. E.g. the app requests
@@ -1707,12 +1708,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// the task can be updated to portrait first so the configuration can be
// computed in a consistent environment.
&& (inMultiWindowMode()
- || !handlesOrientationChangeFromDescendant(orientation))) {
+ || !handlesOrientationChangeFromDescendant(requestedOrientation))) {
// Resolve the requested orientation.
onConfigurationChanged(parent.getConfiguration());
}
onDescendantOrientationChanged(requestingContainer);
- return resolvedOrientation;
+ return actualOverrideOrientation;
}
@ScreenOrientation
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 073ee31ddd60..4f9859d33d74 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -51,7 +51,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;
import java.io.FileDescriptor;
@@ -62,17 +61,17 @@ import java.util.List;
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
- private final Context mContext;
-
// TODO(b/362756788): Does this need to be a LockGuard lock?
private final Object mLockDoNoUseDirectly = new Object();
@GuardedBy("getLockObject()")
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
+ private final Context mContext;
private final DevicePolicyManagerInternal mDpmInternal;
private final PackageManager mPackageManager;
private final UserManagerInternal mUserManagerInternal;
+ final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
@@ -82,6 +81,12 @@ public class SupervisionService extends ISupervisionManager.Stub {
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
+ /**
+ * Returns whether supervision is enabled for the given user.
+ *
+ * <p>Supervision is automatically enabled when the supervision app becomes the profile owner or
+ * explicitly enabled via an internal call to {@link #setSupervisionEnabledForUser}.
+ */
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
@@ -92,6 +97,20 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
+ /**
+ * Returns the package name of the active supervision app or null if supervision is disabled.
+ */
+ @Override
+ @Nullable
+ public String getActiveSupervisionAppPackage(@UserIdInt int userId) {
+ if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+ enforcePermission(INTERACT_ACROSS_USERS);
+ }
+ synchronized (getLockObject()) {
+ return getUserDataLocked(userId).supervisionAppPackage;
+ }
+ }
+
@Override
public void onShellCommand(
@Nullable FileDescriptor in,
@@ -140,35 +159,53 @@ public class SupervisionService extends ISupervisionManager.Stub {
return data;
}
- void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+ /**
+ * Sets supervision as enabled or disabled for the given user and, in case supervision is being
+ * enabled, the package of the active supervision app.
+ */
+ private void setSupervisionEnabledForUser(
+ @UserIdInt int userId, boolean enabled, @Nullable String supervisionAppPackage) {
synchronized (getLockObject()) {
- getUserDataLocked(userId).supervisionEnabled = enabled;
+ SupervisionUserData data = getUserDataLocked(userId);
+ data.supervisionEnabled = enabled;
+ data.supervisionAppPackage = enabled ? supervisionAppPackage : null;
}
}
- /** Ensures that supervision is enabled when supervision app is the profile owner. */
+ /** Ensures that supervision is enabled when the supervision app is the profile owner. */
private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
- if (isProfileOwner(userId)) {
- setSupervisionEnabledForUser(userId, true);
+ final ComponentName po =
+ mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
+
+ if (po != null && po.getPackageName().equals(getSystemSupervisionPackage())) {
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
+ } else if (po != null && po.equals(getSupervisionProfileOwnerComponent())) {
+ // TODO(b/392071637): Consider not enabling supervision in case profile owner is given
+ // to the legacy supervision profile owner component.
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
} else {
// TODO(b/381428475): Avoid disabling supervision when the app is not the profile owner.
// This might only be possible after introducing specific and public APIs to enable
- // supervision.
- setSupervisionEnabledForUser(userId, false);
+ // and disable supervision.
+ setSupervisionEnabledForUser(userId, false, /* supervisionAppPackage= */ null);
}
}
- /** Returns whether the supervision app has profile owner status. */
- private boolean isProfileOwner(@UserIdInt int userId) {
- ComponentName profileOwner =
- mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
- return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+ /**
+ * Returns the {@link ComponentName} of the supervision profile owner component.
+ *
+ * <p>This component is used to give GMS Kids Module permission to supervise the device and may
+ * still be active during the transition to the {@code SYSTEM_SUPERVISION} role.
+ */
+ private ComponentName getSupervisionProfileOwnerComponent() {
+ return ComponentName.unflattenFromString(
+ mContext.getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent));
}
- /** Returns whether the given package name belongs to the supervision role holder. */
- private boolean isSupervisionAppPackage(String packageName) {
- return packageName.equals(
- mContext.getResources().getString(R.string.config_systemSupervision));
+ /** Returns the package assigned to the {@code SYSTEM_SUPERVISION} role. */
+ private String getSystemSupervisionPackage() {
+ return mContext.getResources().getString(R.string.config_systemSupervision);
}
/** Enforces that the caller has the given permission. */
@@ -228,19 +265,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
- final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
-
private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
@Override
public boolean isActiveSupervisionApp(int uid) {
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
+ int userId = UserHandle.getUserId(uid);
+ String supervisionAppPackage = getActiveSupervisionAppPackage(userId);
+ if (supervisionAppPackage == null) {
return false;
}
- for (var packageName : packages) {
- if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
- int userId = UserHandle.getUserId(uid);
- return SupervisionService.this.isSupervisionEnabledForUser(userId);
+
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages != null) {
+ for (var packageName : packages) {
+ if (supervisionAppPackage.equals(packageName)) {
+ return true;
+ }
}
}
return false;
@@ -253,7 +292,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
- SupervisionService.this.setSupervisionEnabledForUser(userId, enabled);
+ SupervisionService.this.setSupervisionEnabledForUser(
+ userId, enabled, getSystemSupervisionPackage());
}
@Override
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
index 2adaae3943f1..976642bd563d 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -32,16 +32,18 @@ public class SupervisionServiceShellCommand extends ShellCommand {
return handleDefaultCommands(null);
}
switch (cmd) {
- case "enable": return setEnabled(true);
- case "disable": return setEnabled(false);
- default: return handleDefaultCommands(cmd);
+ case "enable":
+ return setEnabled(true);
+ case "disable":
+ return setEnabled(false);
+ default:
+ return handleDefaultCommands(cmd);
}
}
private int setEnabled(boolean enabled) {
- final var pw = getOutPrintWriter();
final var userId = UserHandle.parseUserArg(getNextArgRequired());
- mService.setSupervisionEnabledForUser(userId, enabled);
+ mService.mInternal.setSupervisionEnabledForUser(userId, enabled);
return 0;
}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
index 1dd48f581bf4..06acb91509a1 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -26,6 +26,7 @@ import android.util.IndentingPrintWriter;
public class SupervisionUserData {
public final @UserIdInt int userId;
public boolean supervisionEnabled;
+ @Nullable public String supervisionAppPackage;
public boolean supervisionLockScreenEnabled;
@Nullable public PersistableBundle supervisionLockScreenOptions;
@@ -38,6 +39,7 @@ public class SupervisionUserData {
pw.println("User " + userId + ":");
pw.increaseIndent();
pw.println("supervisionEnabled: " + supervisionEnabled);
+ pw.println("supervisionAppPackage: " + supervisionAppPackage);
pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
pw.decreaseIndent();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
index 01061f16c279..d9224eaf66ca 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
@@ -29,6 +29,7 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
private val TEST_PLUGIN_TYPE = PluginType(Int::class.java, "test_type")
+private val DISPLAY_ID = "display_id"
@SmallTest
class PluginManagerTest {
@@ -62,18 +63,18 @@ class PluginManagerTest {
fun testSubscribe() {
val pluginManager = createPluginManager()
- pluginManager.subscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.subscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
@Test
fun testUnsubscribe() {
val pluginManager = createPluginManager()
- pluginManager.unsubscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.unsubscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
private fun createPluginManager(enabled: Boolean = true): PluginManager {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
index 218e34134e93..8eb3e9fbf9b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
@@ -23,6 +23,8 @@ import org.junit.Test
private val TEST_PLUGIN_TYPE1 = PluginType(String::class.java, "test_type1")
private val TEST_PLUGIN_TYPE2 = PluginType(String::class.java, "test_type2")
+private val DISPLAY_ID_1 = "display_1"
+private val DISPLAY_ID_2 = "display_2"
@SmallTest
class PluginStorageTest {
@@ -33,9 +35,9 @@ class PluginStorageTest {
fun testUpdateValue() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -44,9 +46,9 @@ class PluginStorageTest {
fun testAddListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -55,10 +57,10 @@ class PluginStorageTest {
fun testRemoveListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
- storage.removeListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
+ storage.removeListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isNull()
}
@@ -68,10 +70,10 @@ class PluginStorageTest {
val type1Value = "value1"
val type2Value = "value2"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
- storage.updateValue(TEST_PLUGIN_TYPE2, type2Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, type2Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -81,15 +83,62 @@ class PluginStorageTest {
val type1Value = "value1"
val testChangeListener1 = TestPluginChangeListener<String>()
val testChangeListener2 = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener1)
- storage.addListener(TEST_PLUGIN_TYPE2, testChangeListener2)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, testChangeListener2)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
assertThat(testChangeListener2.receivedValue).isNull()
}
+ @Test
+ fun testUpdateGlobal_noDisplaySpecificValue() {
+ val type1Value = "value1"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1Value)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1Value)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValue() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValueRemoved() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, null)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1GlobalValue)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
private class TestPluginChangeListener<T> : PluginChangeListener<T> {
var receivedValue: T? = null
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 3ced56a04138..a58a9cd2a28f 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -34,7 +34,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -53,15 +52,11 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
-import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -76,11 +71,9 @@ import android.media.projection.StopReason;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -99,7 +92,6 @@ import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -110,7 +102,6 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -292,8 +283,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(stoppedCallback2).isFalse();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked() throws Exception {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -308,8 +297,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_packageAllowlisted()
throws NameNotFoundException {
@@ -325,8 +312,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
throws NameNotFoundException {
@@ -347,50 +332,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
- @Test
- public void testCreateProjection_keyguardLocked_RoleHeld() {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
- doReturn(mAppInfo)
- .when(mPackageManager)
- .getApplicationInfoAsUser(
- anyString(),
- any(ApplicationInfoFlags.class),
- any(UserHandle.class));
- MediaProjectionManagerService.MediaProjection projection =
- mService.createProjectionInternal(
- Process.myUid(),
- mContext.getPackageName(),
- TYPE_MIRRORING,
- /* isPermanentGrant= */ false,
- UserHandle.CURRENT,
- DEFAULT_DISPLAY);
- doReturn(true).when(mKeyguardManager).isKeyguardLocked();
- doReturn(PackageManager.PERMISSION_DENIED)
- .when(mPackageManager)
- .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
-
- projection.start(mIMediaProjectionCallback);
- projection.notifyVirtualDisplayCreated(10);
-
- // The projection was started because it was allowed to capture the
- // keyguard.
- assertWithMessage("Failed to run projection")
- .that(mService.getActiveProjectionInfo())
- .isNotNull();
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
- }
-
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled()
throws NameNotFoundException {
@@ -416,8 +357,6 @@ public class MediaProjectionManagerServiceTest {
}
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_noDisplayCreated()
throws NameNotFoundException {
@@ -509,8 +448,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(secondProjection).isNotEqualTo(projection);
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardNotLocked_startConsentDialog()
throws NameNotFoundException {
@@ -527,8 +464,6 @@ public class MediaProjectionManagerServiceTest {
verify(mContext).startActivityAsUser(any(), any());
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -1302,48 +1237,6 @@ public class MediaProjectionManagerServiceTest {
return mService.getProjectionInternal(UID, PACKAGE_NAME);
}
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {});
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
-
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
CountDownLatch mLatch = new CountDownLatch(1);
@Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
index 379079a0018c..10ac0495d69a 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -22,7 +22,6 @@ import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -37,13 +36,10 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -69,7 +65,6 @@ import com.android.server.SystemConfig;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -79,9 +74,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -123,6 +116,8 @@ public class MediaProjectionStopControllerTest {
private KeyguardManager mKeyguardManager;
@Mock
private TelecomManager mTelecomManager;
+ @Mock
+ private MediaProjectionStopController.RoleHolderProvider mRoleManager;
private AppOpsManager mAppOpsManager;
@Mock
@@ -145,7 +140,7 @@ public class MediaProjectionStopControllerTest {
mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
mContext.setMockPackageManager(mPackageManager);
- mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
+ mStopController = new MediaProjectionStopController(mContext, mStopConsumer, mRoleManager);
mService = new MediaProjectionManagerService(mContext,
mMediaProjectionMetricsLoggerInjector);
@@ -170,8 +165,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionNotRestricted() throws Exception {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -180,8 +173,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionRestricted() throws Exception {
MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
mediaProjection.notifyVirtualDisplayCreated(1);
@@ -239,21 +230,13 @@ public class MediaProjectionStopControllerTest {
@Test
public void testExemptFromStoppingHasAppStreamingRole() throws Exception {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- MediaProjectionManagerService.MediaProjection mediaProjection =
- createMediaProjection();
- doReturn(PackageManager.PERMISSION_DENIED).when(
- mPackageManager).checkPermission(
- RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
- assertThat(mStopController.isExemptFromStopping(mediaProjection,
- MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ doReturn(List.of(mediaProjection.packageName)).when(mRoleManager).getRoleHoldersAsUser(
+ eq(AssociationRequest.DEVICE_PROFILE_APP_STREAMING), any(UserHandle.class));
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
}
@Test
@@ -316,8 +299,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_unlocked() {
mStopController.onKeyguardLockedStateChanged(false);
@@ -325,8 +306,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_locked() {
mStopController.onKeyguardLockedStateChanged(true);
@@ -438,47 +417,4 @@ public class MediaProjectionStopControllerTest {
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, mContext.getUser(),
INVALID_DISPLAY);
}
-
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {
- });
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 5862ac65eba9..af50effb7c8e 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -92,6 +92,21 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ simulateUserStarting(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -103,6 +118,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID, preCreated = true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -114,6 +130,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -125,6 +142,21 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun profileOwnerChanged_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ broadcastProfileOwnerChanged(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -136,13 +168,14 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
fun isActiveSupervisionApp_supervisionUid_supervisionEnabled_returnsTrue() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isTrue()
}
@@ -151,7 +184,7 @@ class SupervisionServiceTest {
fun isActiveSupervisionApp_supervisionUid_supervisionNotEnabled_returnsFalse() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
}
@@ -167,15 +200,15 @@ class SupervisionServiceTest {
fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
@Test
- fun supervisionEnabledForUser_internal() {
+ fun setSupervisionEnabledForUser_internal() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
@@ -205,6 +238,13 @@ class SupervisionServiceTest {
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
+ private val supervisionProfileOwnerComponent: ComponentName
+ get() =
+ context
+ .getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent)
+ .let(ComponentName::unflattenFromString)!!
+
private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) {
val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
userInfo.preCreated = preCreated