summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java2
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java11
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl7
-rw-r--r--core/java/android/content/pm/LauncherApps.java28
-rw-r--r--core/java/android/content/pm/PackageInstaller.java4
-rw-r--r--core/java/android/content/pm/parsing/ApkLite.java23
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java10
-rw-r--r--core/java/android/content/pm/parsing/PackageLite.java22
-rw-r--r--core/java/android/hardware/display/DisplayManager.java6
-rw-r--r--core/java/android/window/IDumpCallback.aidl24
-rw-r--r--core/res/res/drawable-hdpi/pointer_copy.pngbin1543 -> 1641 bytes
-rw-r--r--core/res/res/drawable-hdpi/pointer_grab.pngbin971 -> 1014 bytes
-rw-r--r--core/res/res/drawable-hdpi/pointer_grabbing.pngbin847 -> 886 bytes
-rw-r--r--core/res/res/drawable-hdpi/pointer_hand.pngbin954 -> 1014 bytes
-rw-r--r--core/res/res/drawable-hdpi/pointer_nodrop.pngbin1719 -> 1662 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_copy.pngbin932 -> 920 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_copy_large.pngbin3183 -> 3217 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_grab.pngbin546 -> 580 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_grab_large.pngbin2001 -> 2010 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_grabbing.pngbin471 -> 521 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_grabbing_large.pngbin1760 -> 1826 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_hand.pngbin519 -> 561 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_hand_large.pngbin2020 -> 2015 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_nodrop.pngbin994 -> 943 bytes
-rw-r--r--core/res/res/drawable-mdpi/pointer_nodrop_large.pngbin3520 -> 3270 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_copy.pngbin2262 -> 2376 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_copy_large.pngbin8043 -> 8457 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_grab.pngbin1412 -> 1483 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_grab_large.pngbin5624 -> 5838 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_grabbing.pngbin1217 -> 1292 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_grabbing_large.pngbin4914 -> 5094 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_hand.pngbin1390 -> 1437 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_hand_large.pngbin5654 -> 5852 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_nodrop.pngbin2486 -> 2377 bytes
-rw-r--r--core/res/res/drawable-xhdpi/pointer_nodrop_large.pngbin8899 -> 8612 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/pointer_copy.pngbin3886 -> 4169 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/pointer_grab.pngbin2623 -> 2709 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/pointer_grabbing.pngbin2252 -> 2355 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/pointer_hand.pngbin2555 -> 2692 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/pointer_nodrop.pngbin4356 -> 4156 bytes
-rw-r--r--core/res/res/drawable/pointer_copy_icon.xml2
-rw-r--r--core/res/res/drawable/pointer_copy_large_icon.xml4
-rw-r--r--core/res/res/drawable/pointer_grab_icon.xml4
-rw-r--r--core/res/res/drawable/pointer_grab_large_icon.xml4
-rw-r--r--core/res/res/drawable/pointer_grabbing_large_icon.xml4
-rw-r--r--core/res/res/drawable/pointer_hand_icon.xml2
-rw-r--r--core/res/res/drawable/pointer_hand_large_icon.xml2
-rw-r--r--core/res/res/drawable/pointer_nodrop_icon.xml2
-rw-r--r--core/res/res/drawable/pointer_nodrop_large_icon.xml4
-rw-r--r--libs/WindowManager/Shell/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp28
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp5
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h2
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp10
-rw-r--r--libs/hwui/renderthread/VulkanSurface.h2
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/jni/android_media_MediaCodec.cpp263
-rw-r--r--media/jni/android_media_MediaCodec.h2
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt7
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt3
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt4
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt7
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/NetworkTypeIconModel.kt69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt1166
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt162
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt42
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java6
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java16
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java2
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java31
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java10
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java126
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java14
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java171
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java56
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java20
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java18
-rw-r--r--services/core/java/com/android/server/display/layout/Layout.java4
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java86
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java44
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java2
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java33
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java708
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java3
134 files changed, 2436 insertions, 1539 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 6dcf33114b19..808f25eb7cd9 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -860,10 +860,10 @@ public class AccessibilityServiceInfo implements Parcelable {
* <p>
* <strong>Generated by the system.</strong>
* </p>
- * @return The id.
+ * @return The id (or {@code null} if the component is not set yet).
*/
public String getId() {
- return mComponentName.flattenToShortString();
+ return mComponentName == null ? null : mComponentName.flattenToShortString();
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ffc661638eb8..7dabe60e3ba8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6413,7 +6413,7 @@ public class DevicePolicyManager {
public void lockNow(@LockNowFlag int flags) {
if (mService != null) {
try {
- mService.lockNow(flags, mParentInstance);
+ mService.lockNow(flags, mContext.getPackageName(), mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8d508c0fb79d..9b0b18ac74ec 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -119,7 +119,7 @@ interface IDevicePolicyManager {
void setRequiredStrongAuthTimeout(in ComponentName who, String callerPackageName, long timeMs, boolean parent);
long getRequiredStrongAuthTimeout(in ComponentName who, int userId, boolean parent);
- void lockNow(int flags, boolean parent);
+ void lockNow(int flags, String callerPackageName, boolean parent);
/**
* @param factoryReset only applicable when `targetSdk >= U`, either tries to factoryReset/fail or removeUser/fail otherwise
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index da6784be4404..2ca2b79bcc08 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -64,6 +64,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
@@ -171,6 +172,7 @@ public final class VirtualDeviceManager {
public VirtualDevice createVirtualDevice(
int associationId,
@NonNull VirtualDeviceParams params) {
+ Objects.requireNonNull(params, "params must not be null");
try {
return new VirtualDevice(mService, mContext, associationId, params);
} catch (RemoteException e) {
@@ -409,6 +411,9 @@ public final class VirtualDeviceManager {
@NonNull PendingIntent pendingIntent,
@NonNull Executor executor,
@NonNull IntConsumer listener) {
+ Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+ Objects.requireNonNull(executor, "executor must not be null");
+ Objects.requireNonNull(listener, "listener must not be null");
mVirtualDeviceInternal.launchPendingIntent(
displayId, pendingIntent, executor, listener);
}
@@ -483,6 +488,7 @@ public final class VirtualDeviceManager {
@NonNull VirtualDisplayConfig config,
@Nullable @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
+ Objects.requireNonNull(config, "config must not be null");
return mVirtualDeviceInternal.createVirtualDisplay(config, executor, callback);
}
@@ -503,6 +509,7 @@ public final class VirtualDeviceManager {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
+ Objects.requireNonNull(config, "config must not be null");
return mVirtualDeviceInternal.createVirtualDpad(config);
}
@@ -514,6 +521,7 @@ public final class VirtualDeviceManager {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) {
+ Objects.requireNonNull(config, "config must not be null");
return mVirtualDeviceInternal.createVirtualKeyboard(config);
}
@@ -550,6 +558,7 @@ public final class VirtualDeviceManager {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) {
+ Objects.requireNonNull(config, "config must not be null");
return mVirtualDeviceInternal.createVirtualMouse(config);
}
@@ -587,6 +596,7 @@ public final class VirtualDeviceManager {
@NonNull
public VirtualTouchscreen createVirtualTouchscreen(
@NonNull VirtualTouchscreenConfig config) {
+ Objects.requireNonNull(config, "config must not be null");
return mVirtualDeviceInternal.createVirtualTouchscreen(config);
}
@@ -659,6 +669,7 @@ public final class VirtualDeviceManager {
@NonNull VirtualDisplay display,
@Nullable Executor executor,
@Nullable AudioConfigurationChangeCallback callback) {
+ Objects.requireNonNull(display, "display must not be null");
return mVirtualDeviceInternal.createVirtualAudioDevice(display, executor, callback);
}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 96a42e24bc1a..563ed7dd6e7a 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -38,6 +38,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.ParcelFileDescriptor;
+import android.window.IDumpCallback;
import com.android.internal.infra.AndroidFuture;
@@ -116,4 +117,10 @@ interface ILauncherApps {
String getShortcutIconUri(String callingPackage, String packageName, String shortcutId,
int userId);
Map<String, LauncherActivityInfoInternal> getActivityOverrides(String callingPackage, int userId);
+
+ /** Register a callback to be called right before the wmtrace data is moved to the bugreport. */
+ void registerDumpCallback(IDumpCallback cb);
+
+ /** Unregister a callback, so that it won't be called when LauncherApps dumps. */
+ void unRegisterDumpCallback(IDumpCallback cb);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8989006a7e83..27270d9f378f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -17,6 +17,7 @@
package android.content.pm;
import static android.Manifest.permission;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -68,6 +69,7 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
+import android.window.IDumpCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
@@ -1172,6 +1174,32 @@ public class LauncherApps {
}
/**
+ * Register a callback to be called right before the wmtrace data is moved to the bugreport.
+ * @hide
+ */
+ @RequiresPermission(READ_FRAME_BUFFER)
+ public void registerDumpCallback(IDumpCallback cb) {
+ try {
+ mService.registerDumpCallback(cb);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregister a callback, so that it won't be called when LauncherApps dumps.
+ * @hide
+ */
+ @RequiresPermission(READ_FRAME_BUFFER)
+ public void unRegisterDumpCallback(IDumpCallback cb) {
+ try {
+ mService.unRegisterDumpCallback(cb);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns {@link ShortcutInfo}s that match {@code query}.
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index cb988dfdb203..30fd77ca467c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2991,6 +2991,10 @@ public class PackageInstaller {
* The update ownership enforcement can only be enabled on initial installation. Set
* this to {@code true} on package update is a no-op.
*
+ * Apps may opt themselves out of update ownership by setting the
+ * <a href="https://developer.android.com/guide/topics/manifest/manifest-element.html#allowupdateownership">android:alllowUpdateOwnership</a>
+ * attribute in their manifest to <code>false</code>.
+ *
* Note: To enable the update ownership enforcement, the installer must have the
* {@link android.Manifest.permission#ENFORCE_UPDATE_OWNERSHIP ENFORCE_UPDATE_OWNERSHIP}
* permission.
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 269bec256282..408f7ed9f766 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -138,6 +138,11 @@ public class ApkLite {
*/
private final boolean mIsSdkLibrary;
+ /**
+ * Indicates if this package allows an installer to declare update ownership of it.
+ */
+ private final boolean mAllowUpdateOwnership;
+
public ApkLite(String path, String packageName, String splitName, boolean isFeatureSplit,
String configForSplit, String usesSplitName, boolean isSplitRequired, int versionCode,
int versionCodeMajor, int revisionCode, int installLocation,
@@ -148,7 +153,7 @@ public class ApkLite {
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary) {
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean allowUpdateOwnership) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -182,6 +187,7 @@ public class ApkLite {
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
+ mAllowUpdateOwnership = allowUpdateOwnership;
}
/**
@@ -474,6 +480,9 @@ public class ApkLite {
return mRollbackDataPolicy;
}
+ /**
+ * Indicates if this app contains a {@link android.app.admin.DeviceAdminReceiver}.
+ */
@DataClass.Generated.Member
public boolean isHasDeviceAdminReceiver() {
return mHasDeviceAdminReceiver;
@@ -487,11 +496,19 @@ public class ApkLite {
return mIsSdkLibrary;
}
+ /**
+ * Indicates if this package allows an installer to declare update ownership of it.
+ */
+ @DataClass.Generated.Member
+ public boolean isAllowUpdateOwnership() {
+ return mAllowUpdateOwnership;
+ }
+
@DataClass.Generated(
- time = 1643063342990L,
+ time = 1680122754650L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mAllowUpdateOwnership\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 64fed63c7159..a4339d41dfd2 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -127,7 +127,8 @@ public class ApkLiteParseUtils {
null /* isFeatureSplits */, null /* usesSplitNames */,
null /* configForSplit */, null /* splitApkPaths */,
null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
- null /* requiredSplitTypes */, null /* splitTypes */));
+ null /* requiredSplitTypes */, null, /* splitTypes */
+ baseApk.isAllowUpdateOwnership()));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -273,7 +274,8 @@ public class ApkLiteParseUtils {
return input.success(
new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes,
- baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes));
+ baseApk.getTargetSdkVersion(), requiredSplitTypes, splitTypes,
+ baseApk.isAllowUpdateOwnership()));
}
/**
@@ -400,6 +402,8 @@ public class ApkLiteParseUtils {
"isFeatureSplit", false);
boolean isSplitRequired = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"isSplitRequired", false);
+ boolean allowUpdateOwnership = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
+ "allowUpdateOwnership", true);
String configForSplit = parser.getAttributeValue(null, "configForSplit");
int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
@@ -583,7 +587,7 @@ public class ApkLiteParseUtils {
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary));
+ hasDeviceAdminReceiver, isSdkLibrary, allowUpdateOwnership));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index e2789c93516f..e24b9320110e 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -110,10 +110,16 @@ public class PackageLite {
*/
private final boolean mIsSdkLibrary;
+ /**
+ * Indicates if this package allows an installer to declare update ownership of it.
+ */
+ private final boolean mAllowUpdateOwnership;
+
public PackageLite(String path, String baseApkPath, ApkLite baseApk,
String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes,
- int targetSdk, Set<String>[] requiredSplitTypes, Set<String>[] splitTypes) {
+ int targetSdk, Set<String>[] requiredSplitTypes, Set<String>[] splitTypes,
+ boolean allowUpdateOwnership) {
// The following paths may be different from the path in ApkLite because we
// move or rename the APK files. Use parameters to indicate the correct paths.
mPath = path;
@@ -144,6 +150,7 @@ public class PackageLite {
mSplitApkPaths = splitApkPaths;
mSplitRevisionCodes = splitRevisionCodes;
mTargetSdk = targetSdk;
+ mAllowUpdateOwnership = allowUpdateOwnership;
}
/**
@@ -414,12 +421,19 @@ public class PackageLite {
return mIsSdkLibrary;
}
+ /**
+ * Indicates if this package allows an installer to declare update ownership of it.
+ */
+ @DataClass.Generated.Member
+ public boolean isAllowUpdateOwnership() {
+ return mAllowUpdateOwnership;
+ }
+
@DataClass.Generated(
- time = 1643132127068L,
+ time = 1680125514341L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures =
- "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final boolean mAllowUpdateOwnership\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b5281a5025b8..2aead3c22deb 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1758,10 +1758,12 @@ public final class DisplayManager {
/**
* Key for the brightness throttling data as a String formatted:
* <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
- * Where the latter part is repeated for each throttling level, and the entirety is repeated
- * for each display, separated by a semicolon.
+ * [,<throttlingId>]?
+ * Where [<severity as string>,<brightness cap>] is repeated for each throttling level.
+ * The entirety is repeated for each display and throttling id, separated by a semicolon.
* For example:
* 123,1,critical,0.8;456,2,moderate,0.9,critical,0.7
+ * 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7
*/
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
}
diff --git a/core/java/android/window/IDumpCallback.aidl b/core/java/android/window/IDumpCallback.aidl
new file mode 100644
index 000000000000..4c825d43add1
--- /dev/null
+++ b/core/java/android/window/IDumpCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package android.window;
+
+/**
+ * Callback for processes which need to feed data to another process when it dumps.
+ * @hide
+ */
+ interface IDumpCallback {
+ oneway void onDump(in ParcelFileDescriptor outFd);
+ } \ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/pointer_copy.png b/core/res/res/drawable-hdpi/pointer_copy.png
index c5eda2e5b5c4..5d06a8eaa551 100644
--- a/core/res/res/drawable-hdpi/pointer_copy.png
+++ b/core/res/res/drawable-hdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_grab.png b/core/res/res/drawable-hdpi/pointer_grab.png
index 26da04de1d12..b76ec16e243b 100644
--- a/core/res/res/drawable-hdpi/pointer_grab.png
+++ b/core/res/res/drawable-hdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_grabbing.png b/core/res/res/drawable-hdpi/pointer_grabbing.png
index f4031a9227c7..10013e9a1821 100644
--- a/core/res/res/drawable-hdpi/pointer_grabbing.png
+++ b/core/res/res/drawable-hdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_hand.png b/core/res/res/drawable-hdpi/pointer_hand.png
index a7ae55fc4b5f..8a7277477ab0 100644
--- a/core/res/res/drawable-hdpi/pointer_hand.png
+++ b/core/res/res/drawable-hdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_nodrop.png b/core/res/res/drawable-hdpi/pointer_nodrop.png
index 7043323701dd..9df140c5816b 100644
--- a/core/res/res/drawable-hdpi/pointer_nodrop.png
+++ b/core/res/res/drawable-hdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_copy.png b/core/res/res/drawable-mdpi/pointer_copy.png
index e731108370d3..7189dada1bf2 100644
--- a/core/res/res/drawable-mdpi/pointer_copy.png
+++ b/core/res/res/drawable-mdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_copy_large.png b/core/res/res/drawable-mdpi/pointer_copy_large.png
index 15ccb04b8f76..a3d487df9e5e 100644
--- a/core/res/res/drawable-mdpi/pointer_copy_large.png
+++ b/core/res/res/drawable-mdpi/pointer_copy_large.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grab.png b/core/res/res/drawable-mdpi/pointer_grab.png
index d625b55f7066..977b36cd875a 100644
--- a/core/res/res/drawable-mdpi/pointer_grab.png
+++ b/core/res/res/drawable-mdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grab_large.png b/core/res/res/drawable-mdpi/pointer_grab_large.png
index 9d36df0d6d94..80587ceb8aba 100644
--- a/core/res/res/drawable-mdpi/pointer_grab_large.png
+++ b/core/res/res/drawable-mdpi/pointer_grab_large.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grabbing.png b/core/res/res/drawable-mdpi/pointer_grabbing.png
index 71bb17ba5592..2bdcbdc7af75 100644
--- a/core/res/res/drawable-mdpi/pointer_grabbing.png
+++ b/core/res/res/drawable-mdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_grabbing_large.png b/core/res/res/drawable-mdpi/pointer_grabbing_large.png
index 5574b07faf44..a8a599c5ebee 100644
--- a/core/res/res/drawable-mdpi/pointer_grabbing_large.png
+++ b/core/res/res/drawable-mdpi/pointer_grabbing_large.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_hand.png b/core/res/res/drawable-mdpi/pointer_hand.png
index d7f7beda111c..e94b927cada6 100644
--- a/core/res/res/drawable-mdpi/pointer_hand.png
+++ b/core/res/res/drawable-mdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_hand_large.png b/core/res/res/drawable-mdpi/pointer_hand_large.png
index f775464ced18..7d89067b98bd 100644
--- a/core/res/res/drawable-mdpi/pointer_hand_large.png
+++ b/core/res/res/drawable-mdpi/pointer_hand_large.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_nodrop.png b/core/res/res/drawable-mdpi/pointer_nodrop.png
index 931b74094d79..15764fa4e51b 100644
--- a/core/res/res/drawable-mdpi/pointer_nodrop.png
+++ b/core/res/res/drawable-mdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_nodrop_large.png b/core/res/res/drawable-mdpi/pointer_nodrop_large.png
index 88f77d300a15..46ff5f779cef 100644
--- a/core/res/res/drawable-mdpi/pointer_nodrop_large.png
+++ b/core/res/res/drawable-mdpi/pointer_nodrop_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_copy.png b/core/res/res/drawable-xhdpi/pointer_copy.png
index 5b6cc5bc454f..8d889e1ad131 100644
--- a/core/res/res/drawable-xhdpi/pointer_copy.png
+++ b/core/res/res/drawable-xhdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_copy_large.png b/core/res/res/drawable-xhdpi/pointer_copy_large.png
index d78a410a2887..860c85499617 100644
--- a/core/res/res/drawable-xhdpi/pointer_copy_large.png
+++ b/core/res/res/drawable-xhdpi/pointer_copy_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grab.png b/core/res/res/drawable-xhdpi/pointer_grab.png
index 46dd3eeb95c6..dd3c6de46be5 100644
--- a/core/res/res/drawable-xhdpi/pointer_grab.png
+++ b/core/res/res/drawable-xhdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grab_large.png b/core/res/res/drawable-xhdpi/pointer_grab_large.png
index 1c7e63e2e527..bcae2c943d12 100644
--- a/core/res/res/drawable-xhdpi/pointer_grab_large.png
+++ b/core/res/res/drawable-xhdpi/pointer_grab_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing.png b/core/res/res/drawable-xhdpi/pointer_grabbing.png
index 2fb8a9c4442f..500fa73f58e4 100644
--- a/core/res/res/drawable-xhdpi/pointer_grabbing.png
+++ b/core/res/res/drawable-xhdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
index 3467a03e2a56..c9c6f187e2a8 100644
--- a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
+++ b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_hand.png b/core/res/res/drawable-xhdpi/pointer_hand.png
index 926310cce74d..e931afe4eec8 100644
--- a/core/res/res/drawable-xhdpi/pointer_hand.png
+++ b/core/res/res/drawable-xhdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_hand_large.png b/core/res/res/drawable-xhdpi/pointer_hand_large.png
index 546b222164b5..39ae1b7438d0 100644
--- a/core/res/res/drawable-xhdpi/pointer_hand_large.png
+++ b/core/res/res/drawable-xhdpi/pointer_hand_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop.png b/core/res/res/drawable-xhdpi/pointer_nodrop.png
index fdfc2671255c..4c9fe63136a8 100644
--- a/core/res/res/drawable-xhdpi/pointer_nodrop.png
+++ b/core/res/res/drawable-xhdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
index 2b5e8a4b0f59..ae60e77bbc5e 100644
--- a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
+++ b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/pointer_copy.png b/core/res/res/drawable-xxhdpi/pointer_copy.png
index bef4fb4e8c17..24661a653f42 100644
--- a/core/res/res/drawable-xxhdpi/pointer_copy.png
+++ b/core/res/res/drawable-xxhdpi/pointer_copy.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/pointer_grab.png b/core/res/res/drawable-xxhdpi/pointer_grab.png
index 6caa1ba77eb9..1917722fa4fa 100644
--- a/core/res/res/drawable-xxhdpi/pointer_grab.png
+++ b/core/res/res/drawable-xxhdpi/pointer_grab.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/pointer_grabbing.png b/core/res/res/drawable-xxhdpi/pointer_grabbing.png
index b52f75174a16..a4cd9e56ba2f 100644
--- a/core/res/res/drawable-xxhdpi/pointer_grabbing.png
+++ b/core/res/res/drawable-xxhdpi/pointer_grabbing.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/pointer_hand.png b/core/res/res/drawable-xxhdpi/pointer_hand.png
index f3ee077b5cc9..917529b9aa2a 100644
--- a/core/res/res/drawable-xxhdpi/pointer_hand.png
+++ b/core/res/res/drawable-xxhdpi/pointer_hand.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/pointer_nodrop.png b/core/res/res/drawable-xxhdpi/pointer_nodrop.png
index ef54301e4eac..35b4d13bd0be 100644
--- a/core/res/res/drawable-xxhdpi/pointer_nodrop.png
+++ b/core/res/res/drawable-xxhdpi/pointer_nodrop.png
Binary files differ
diff --git a/core/res/res/drawable/pointer_copy_icon.xml b/core/res/res/drawable/pointer_copy_icon.xml
index da32939b5b0f..7e7c0caf6d78 100644
--- a/core/res/res/drawable/pointer_copy_icon.xml
+++ b/core/res/res/drawable/pointer_copy_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_copy"
- android:hotSpotX="7.5dp"
+ android:hotSpotX="8.5dp"
android:hotSpotY="7.5dp" />
diff --git a/core/res/res/drawable/pointer_copy_large_icon.xml b/core/res/res/drawable/pointer_copy_large_icon.xml
index 55d47b4f4d63..54e95238f501 100644
--- a/core/res/res/drawable/pointer_copy_large_icon.xml
+++ b/core/res/res/drawable/pointer_copy_large_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_copy_large"
- android:hotSpotX="22.5dp"
- android:hotSpotY="22.5dp" />
+ android:hotSpotX="21.25dp"
+ android:hotSpotY="18.75dp" />
diff --git a/core/res/res/drawable/pointer_grab_icon.xml b/core/res/res/drawable/pointer_grab_icon.xml
index b3d4e78828cd..dd1216a2995b 100644
--- a/core/res/res/drawable/pointer_grab_icon.xml
+++ b/core/res/res/drawable/pointer_grab_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_grab"
- android:hotSpotX="13.5dp"
- android:hotSpotY="13.5dp" />
+ android:hotSpotX="9.5dp"
+ android:hotSpotY="4.5dp" />
diff --git a/core/res/res/drawable/pointer_grab_large_icon.xml b/core/res/res/drawable/pointer_grab_large_icon.xml
index 343c7d27c749..b5dbd11fb286 100644
--- a/core/res/res/drawable/pointer_grab_large_icon.xml
+++ b/core/res/res/drawable/pointer_grab_large_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_grab_large"
- android:hotSpotX="33.75dp"
- android:hotSpotY="33.75dp" />
+ android:hotSpotX="23.75dp"
+ android:hotSpotY="11.25dp" />
diff --git a/core/res/res/drawable/pointer_grabbing_large_icon.xml b/core/res/res/drawable/pointer_grabbing_large_icon.xml
index ac1626530778..b041c8397771 100644
--- a/core/res/res/drawable/pointer_grabbing_large_icon.xml
+++ b/core/res/res/drawable/pointer_grabbing_large_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_grabbing_large"
- android:hotSpotX="25.5dp"
- android:hotSpotY="22.5dp" />
+ android:hotSpotX="21.25dp"
+ android:hotSpotY="18.75dp" />
diff --git a/core/res/res/drawable/pointer_hand_icon.xml b/core/res/res/drawable/pointer_hand_icon.xml
index 3f9d1a639ddb..0feccd296406 100644
--- a/core/res/res/drawable/pointer_hand_icon.xml
+++ b/core/res/res/drawable/pointer_hand_icon.xml
@@ -2,4 +2,4 @@
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_hand"
android:hotSpotX="9.5dp"
- android:hotSpotY="1.5dp" />
+ android:hotSpotY="2.5dp" />
diff --git a/core/res/res/drawable/pointer_hand_large_icon.xml b/core/res/res/drawable/pointer_hand_large_icon.xml
index cd49762bd23f..6257b0ba2fb1 100644
--- a/core/res/res/drawable/pointer_hand_large_icon.xml
+++ b/core/res/res/drawable/pointer_hand_large_icon.xml
@@ -2,4 +2,4 @@
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_hand_large"
android:hotSpotX="23.75dp"
- android:hotSpotY="3.75dp" />
+ android:hotSpotY="6.25dp" />
diff --git a/core/res/res/drawable/pointer_nodrop_icon.xml b/core/res/res/drawable/pointer_nodrop_icon.xml
index 4dffd23638da..fb78d7469120 100644
--- a/core/res/res/drawable/pointer_nodrop_icon.xml
+++ b/core/res/res/drawable/pointer_nodrop_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_nodrop"
- android:hotSpotX="7.5dp"
+ android:hotSpotX="8.5dp"
android:hotSpotY="7.5dp" />
diff --git a/core/res/res/drawable/pointer_nodrop_large_icon.xml b/core/res/res/drawable/pointer_nodrop_large_icon.xml
index 602c744401a9..2385e11109b0 100644
--- a/core/res/res/drawable/pointer_nodrop_large_icon.xml
+++ b/core/res/res/drawable/pointer_nodrop_large_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_nodrop_large"
- android:hotSpotX="22.5dp"
- android:hotSpotY="22.5dp" />
+ android:hotSpotX="21.25dp"
+ android:hotSpotY="18.75dp" />
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 852edef544b8..f0ed6ee5cb67 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,4 +1,4 @@
xutan@google.com
# Give submodule owners in shell resource approval
-per-file res*/*/*.xml = hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com
+per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index 926cfb3b12ef..deb7c6db338f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module desktop owners
+atsjenk@google.com
madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 0c2d5c49f830..ccbb9cf298a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module freeform owners
+atsjenk@google.com
madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 318a49a8de31..6d46a9c3d0e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
@@ -310,8 +311,11 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
// TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once possible.
if (mReverseDefaultRotationEnabled) {
setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true,
- /* fromOrientations */ new int[]{SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
- /* toOrientations */ new int[]{SCREEN_ORIENTATION_LANDSCAPE});
+ /* fromOrientations */
+ new int[]{SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
+ /* toOrientations */
+ new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
+ SCREEN_ORIENTATION_SENSOR_LANDSCAPE});
} else {
setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true,
/* fromOrientations */ null, /* toOrientations */ null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index 867162be4c6d..24d0b996a3cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -198,7 +198,7 @@ public class PipBoundsAlgorithm {
/**
* @return whether the given {@param aspectRatio} is valid.
*/
- private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
+ public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
return Float.compare(mMinAspectRatio, aspectRatio) <= 0
&& Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 09e050e91c64..a0bd064149d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1259,7 +1259,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
if (mDeferredTaskInfo != null || PipUtils.aspectRatioChanged(params.getAspectRatioFloat(),
mPictureInPictureParams.getAspectRatioFloat())) {
- mPipParamsChangedForwarder.notifyAspectRatioChanged(params.getAspectRatioFloat());
+ if (mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(
+ params.getAspectRatioFloat())) {
+ mPipParamsChangedForwarder.notifyAspectRatioChanged(params.getAspectRatioFloat());
+ } else {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: New aspect ratio is not valid."
+ + " hasAspectRatio=%b"
+ + " aspectRatio=%f",
+ TAG, params.hasSetAspectRatio(), params.getAspectRatioFloat());
+ }
}
if (mDeferredTaskInfo != null
|| PipUtils.remoteActionsChanged(params.getActions(),
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 1c28c3d58ccb..64dfc3ef845d 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -7,3 +7,4 @@ lbill@google.com
madym@google.com
hwwang@google.com
chenghsiuchang@google.com
+atsjenk@google.com
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 8ea71f11e2f0..1f929685b62c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -28,6 +28,7 @@
#include <SkMultiPictureDocument.h>
#include <SkOverdrawCanvas.h>
#include <SkOverdrawColorFilter.h>
+#include <SkPaintFilterCanvas.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
#include <SkRect.h>
@@ -36,15 +37,15 @@
#include <SkStream.h>
#include <SkString.h>
#include <SkTypeface.h>
-#include "include/gpu/GpuTypes.h" // from Skia
#include <android-base/properties.h>
+#include <gui/TraceUtils.h>
#include <unistd.h>
#include <sstream>
-#include <gui/TraceUtils.h>
#include "LightingInfo.h"
#include "VectorDrawable.h"
+#include "include/gpu/GpuTypes.h" // from Skia
#include "thread/CommonPool.h"
#include "tools/SkSharingProc.h"
#include "utils/Color.h"
@@ -449,6 +450,23 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
}
}
+class ForceDitherCanvas : public SkPaintFilterCanvas {
+public:
+ ForceDitherCanvas(SkCanvas* canvas) : SkPaintFilterCanvas(canvas) {}
+
+protected:
+ bool onFilter(SkPaint& paint) const override {
+ paint.setDither(true);
+ return true;
+ }
+
+ void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+ // We unroll the drawable using "this" canvas, so that draw calls contained inside will
+ // get dithering applied
+ drawable->draw(this, matrix);
+ }
+};
+
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
@@ -503,6 +521,12 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip,
canvas->clear(SK_ColorTRANSPARENT);
}
+ std::optional<ForceDitherCanvas> forceDitherCanvas;
+ if (shouldForceDither()) {
+ forceDitherCanvas.emplace(canvas);
+ canvas = &forceDitherCanvas.value();
+ }
+
if (1 == nodes.size()) {
if (!nodes[0]->nothingToDraw()) {
RenderNodeDrawable root(nodes[0].get(), canvas);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index befee8989383..0763b06b53ef 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -98,6 +98,8 @@ protected:
bool isCapturingSkp() const { return mCaptureMode != CaptureMode::None; }
+ virtual bool shouldForceDither() const { return mColorMode != ColorMode::Default; }
+
private:
void renderFrameImpl(const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index c8f2e69ae0a4..6f1b99b95bbd 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -203,6 +203,11 @@ sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThr
return nullptr;
}
+bool SkiaVulkanPipeline::shouldForceDither() const {
+ if (mVkSurface && mVkSurface->isBeyond8Bit()) return false;
+ return SkiaPipeline::shouldForceDither();
+}
+
void SkiaVulkanPipeline::onContextDestroyed() {
if (mVkSurface) {
vulkanManager().destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index d921ddb0d0fb..0713e93bccde 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -63,6 +63,8 @@ public:
protected:
void onContextDestroyed() override;
+ bool shouldForceDither() const override;
+
private:
renderthread::VulkanManager& vulkanManager();
renderthread::VulkanSurface* mVkSurface = nullptr;
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 21b6c44e997e..ae4f0572576e 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -530,6 +530,16 @@ void VulkanSurface::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
}
}
+bool VulkanSurface::isBeyond8Bit() const {
+ switch (mWindowInfo.bufferFormat) {
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index e2ddc6b07768..3b69b73bcab3 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -48,6 +48,8 @@ public:
void setColorSpace(sk_sp<SkColorSpace> colorSpace);
+ bool isBeyond8Bit() const;
+
private:
/*
* All structs/methods in this private section are specifically for use by the VulkanManager
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index f9d4efea57e2..fe5afc5a717e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -268,7 +268,7 @@ interface IAudioService {
boolean isVolumeControlUsingVolumeGroups();
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
- oneway void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register);
+ void registerStreamAliasingDispatcher(IStreamAliasingDispatcher isad, boolean register);
@EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
void setNotifAliasRingForTest(boolean alias);
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 9a4aa3344374..dea7f03d369a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -43,6 +43,8 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <android-base/stringprintf.h>
+
#include <binder/MemoryDealer.h>
#include <cutils/compiler.h>
@@ -1276,7 +1278,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
ALOGE("Could not create MediaCodec.BufferInfo.");
env->ExceptionClear();
}
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Fatal error: could not create MediaCodec.BufferInfo object");
return;
}
@@ -1309,7 +1312,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
ALOGE("Could not create CodecException object.");
env->ExceptionClear();
}
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Fatal error: could not create CodecException object");
return;
}
@@ -1322,7 +1326,9 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
CHECK(msg->findMessage("format", &format));
if (OK != ConvertMessageToMap(env, format, &obj)) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Fatal error: failed to convert format "
+ "from native to Java object");
return;
}
@@ -1353,7 +1359,8 @@ void JMediaCodec::handleFirstTunnelFrameReadyNotification(const sp<AMessage> &ms
status_t err = ConvertMessageToMap(env, data, &obj);
if (err != OK) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Fatal error: failed to convert format from native to Java object");
return;
}
@@ -1374,7 +1381,8 @@ void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) {
status_t err = ConvertMessageToMap(env, data, &obj);
if (err != OK) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Fatal error: failed to convert format from native to Java object");
return;
}
@@ -1385,6 +1393,18 @@ void JMediaCodec::handleFrameRenderedNotification(const sp<AMessage> &msg) {
env->DeleteLocalRef(obj);
}
+std::string JMediaCodec::getExceptionMessage(const char *msg = nullptr) const {
+ if (mCodec == nullptr) {
+ return msg ?: "";
+ }
+ std::string prefix = "";
+ if (msg && msg[0] != '\0') {
+ prefix.append(msg);
+ prefix.append("\n");
+ }
+ return prefix + mCodec->getErrorLog().extract();
+}
+
void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatCallbackNotify:
@@ -1471,9 +1491,17 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
env->Throw(exception);
}
+static std::string GetExceptionMessage(const sp<JMediaCodec> &codec, const char *msg) {
+ if (codec == NULL) {
+ return msg ?: "codec is released already";
+ }
+ return codec->getExceptionMessage(msg);
+}
+
static jint throwExceptionAsNecessary(
JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
- const char *msg = NULL, const sp<ICrypto>& crypto = NULL) {
+ const char *msg = NULL, const sp<ICrypto>& crypto = NULL,
+ const sp<JMediaCodec> &codec = NULL) {
switch (err) {
case OK:
return 0;
@@ -1488,23 +1516,38 @@ static jint throwExceptionAsNecessary(
return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
case INVALID_OPERATION:
- jniThrowException(env, "java/lang/IllegalStateException", msg);
+ jniThrowException(
+ env, "java/lang/IllegalStateException",
+ GetExceptionMessage(codec, msg).c_str());
return 0;
case BAD_VALUE:
- jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+ jniThrowException(
+ env, "java/lang/IllegalArgumentException",
+ GetExceptionMessage(codec, msg).c_str());
return 0;
default:
if (isCryptoError(err)) {
- throwCryptoException(env, err, msg, crypto);
+ throwCryptoException(
+ env, err,
+ GetExceptionMessage(codec, msg).c_str(),
+ crypto);
return 0;
}
- throwCodecException(env, err, actionCode, msg);
+ throwCodecException(
+ env, err, actionCode,
+ GetExceptionMessage(codec, msg).c_str());
return 0;
}
}
+static jint throwExceptionAsNecessary(
+ JNIEnv *env, status_t err, const sp<JMediaCodec> &codec,
+ int32_t actionCode = ACTION_CODE_FATAL) {
+ return throwExceptionAsNecessary(env, err, actionCode, NULL, NULL, codec);
+}
+
static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListener(
JNIEnv *env,
jobject thiz,
@@ -1512,13 +1555,13 @@ static void android_media_MediaCodec_native_enableOnFirstTunnelFrameReadyListene
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->enableOnFirstTunnelFrameReadyListener(enabled);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
@@ -1528,13 +1571,13 @@ static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->enableOnFrameRenderedListener(enabled);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_native_setCallback(
@@ -1544,13 +1587,13 @@ static void android_media_MediaCodec_native_setCallback(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->setCallback(cb);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_native_configure(
@@ -1564,7 +1607,7 @@ static void android_media_MediaCodec_native_configure(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -1602,7 +1645,7 @@ static void android_media_MediaCodec_native_configure(
err = codec->configure(format, bufferProducer, crypto, descrambler, flags);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_native_setSurface(
@@ -1612,7 +1655,7 @@ static void android_media_MediaCodec_native_setSurface(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -1631,7 +1674,7 @@ static void android_media_MediaCodec_native_setSurface(
}
status_t err = codec->setSurface(bufferProducer);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
@@ -1735,7 +1778,7 @@ static void android_media_MediaCodec_setInputSurface(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -1749,7 +1792,7 @@ static void android_media_MediaCodec_setInputSurface(
}
status_t err = codec->setInputSurface(persistentSurface);
if (err != NO_ERROR) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
}
@@ -1759,7 +1802,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -1767,7 +1810,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
sp<IGraphicBufferProducer> bufferProducer;
status_t err = codec->createInputSurface(&bufferProducer);
if (err != NO_ERROR) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
return NULL;
}
@@ -1782,13 +1825,13 @@ static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->start();
- throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed");
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
@@ -1797,13 +1840,13 @@ static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->stop();
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
@@ -1812,7 +1855,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -1825,7 +1868,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
// trigger an IllegalStateException.
err = UNKNOWN_ERROR;
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
@@ -1834,13 +1877,13 @@ static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->flush();
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_queueInputBuffer(
@@ -1856,7 +1899,7 @@ static void android_media_MediaCodec_queueInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -1866,7 +1909,8 @@ static void android_media_MediaCodec_queueInputBuffer(
index, offset, size, timestampUs, flags, &errorDetailMsg);
throwExceptionAsNecessary(
- env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
+ env, err, ACTION_CODE_FATAL,
+ codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
}
struct NativeCryptoInfo {
@@ -1890,7 +1934,9 @@ struct NativeCryptoInfo {
} else if (jmode == gCryptoModes.AesCbc) {
mMode = CryptoPlugin::kMode_AES_CBC;
} else {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ base::StringPrintf("unrecognized crypto mode: %d", jmode).c_str());
return;
}
@@ -2026,7 +2072,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -2056,7 +2102,9 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
} else if (jmode == gCryptoModes.AesCbc) {
mode = CryptoPlugin::kMode_AES_CBC;
} else {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ base::StringPrintf("Unrecognized crypto mode: %d", jmode).c_str());
return;
}
@@ -2175,8 +2223,8 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
subSamples = NULL;
throwExceptionAsNecessary(
- env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(),
- codec->getCrypto());
+ env, err, ACTION_CODE_FATAL,
+ codec->getExceptionMessage(errorDetailMsg.c_str()).c_str(), codec->getCrypto());
}
static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
@@ -2518,14 +2566,16 @@ static void android_media_MediaCodec_native_queueLinearBlock(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == nullptr || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
sp<AMessage> tunings;
status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
if (err != OK) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(
+ env, err, ACTION_CODE_FATAL,
+ "error occurred while converting tunings from Java to native");
return;
}
@@ -2545,15 +2595,23 @@ static void android_media_MediaCodec_native_queueLinearBlock(
}
env->MonitorExit(lock.get());
} else {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ "Failed to grab lock for a LinearBlock object");
return;
}
AString errorDetailMsg;
if (codec->hasCryptoOrDescrambler()) {
if (!memory) {
+ // It means there was an unexpected failure in extractMemoryFromContext above
ALOGI("queueLinearBlock: no ashmem memory for encrypted content");
- throwExceptionAsNecessary(env, BAD_VALUE);
+ throwExceptionAsNecessary(
+ env, BAD_VALUE, ACTION_CODE_FATAL,
+ "Unexpected error: the input buffer is not compatible with "
+ "the secure codec, and a fallback logic failed.\n"
+ "Suggestion: please try including the secure codec when calling "
+ "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
return;
}
auto cryptoInfo =
@@ -2577,14 +2635,22 @@ static void android_media_MediaCodec_native_queueLinearBlock(
ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err);
} else {
if (!buffer) {
+ // It means there was an unexpected failure in extractBufferFromContext above
ALOGI("queueLinearBlock: no C2Buffer found");
- throwExceptionAsNecessary(env, BAD_VALUE);
+ throwExceptionAsNecessary(
+ env, BAD_VALUE, ACTION_CODE_FATAL,
+ "Unexpected error: the input buffer is not compatible with "
+ "the non-secure codec, and a fallback logic failed.\n"
+ "Suggestion: please do not include the secure codec when calling "
+ "MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
return;
}
err = codec->queueBuffer(
index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
}
- throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+ throwExceptionAsNecessary(
+ env, err, ACTION_CODE_FATAL,
+ codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
}
static void android_media_MediaCodec_native_queueHardwareBuffer(
@@ -2595,14 +2661,16 @@ static void android_media_MediaCodec_native_queueHardwareBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
sp<AMessage> tunings;
status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
if (err != OK) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(
+ env, err, ACTION_CODE_FATAL,
+ "error occurred while converting tunings from Java to native");
return;
}
@@ -2627,7 +2695,9 @@ static void android_media_MediaCodec_native_queueHardwareBuffer(
ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation");
native_handle_close(handle);
native_handle_delete(handle);
- throwExceptionAsNecessary(env, BAD_VALUE);
+ throwExceptionAsNecessary(
+ env, BAD_VALUE, ACTION_CODE_FATAL,
+ "HardwareBuffer not recognized");
return;
}
std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -2636,7 +2706,9 @@ static void android_media_MediaCodec_native_queueHardwareBuffer(
AString errorDetailMsg;
err = codec->queueBuffer(
index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
- throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+ throwExceptionAsNecessary(
+ env, err, ACTION_CODE_FATAL,
+ codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
}
static void android_media_MediaCodec_native_getOutputFrame(
@@ -2646,13 +2718,13 @@ static void android_media_MediaCodec_native_getOutputFrame(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->getOutputFrame(env, frame, index);
if (err != OK) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
}
@@ -2663,7 +2735,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return -1;
}
@@ -2674,7 +2746,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer(
return (jint) index;
}
- return throwExceptionAsNecessary(env, err);
+ return throwExceptionAsNecessary(env, err, codec);
}
static jint android_media_MediaCodec_dequeueOutputBuffer(
@@ -2684,7 +2756,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return 0;
}
@@ -2696,7 +2768,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer(
return (jint) index;
}
- return throwExceptionAsNecessary(env, err);
+ return throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_releaseOutputBuffer(
@@ -2707,13 +2779,13 @@ static void android_media_MediaCodec_releaseOutputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
@@ -2722,13 +2794,13 @@ static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t err = codec->signalEndOfInputStream();
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static jobject android_media_MediaCodec_getFormatNative(
@@ -2738,7 +2810,7 @@ static jobject android_media_MediaCodec_getFormatNative(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2749,7 +2821,7 @@ static jobject android_media_MediaCodec_getFormatNative(
return format;
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
return NULL;
}
@@ -2761,7 +2833,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2772,7 +2844,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
return format;
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
return NULL;
}
@@ -2784,7 +2856,7 @@ static jobjectArray android_media_MediaCodec_getBuffers(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2797,7 +2869,7 @@ static jobjectArray android_media_MediaCodec_getBuffers(
// if we're out of memory, an exception was already thrown
if (err != NO_MEMORY) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
return NULL;
@@ -2810,7 +2882,7 @@ static jobject android_media_MediaCodec_getBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2823,7 +2895,7 @@ static jobject android_media_MediaCodec_getBuffer(
// if we're out of memory, an exception was already thrown
if (err != NO_MEMORY) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
return NULL;
@@ -2836,7 +2908,7 @@ static jobject android_media_MediaCodec_getImage(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2849,7 +2921,7 @@ static jobject android_media_MediaCodec_getImage(
// if we're out of memory, an exception was already thrown
if (err != NO_MEMORY) {
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
return NULL;
@@ -2862,7 +2934,7 @@ static jobject android_media_MediaCodec_getName(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2873,7 +2945,7 @@ static jobject android_media_MediaCodec_getName(
return name;
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
return NULL;
}
@@ -2885,7 +2957,7 @@ static jobject android_media_MediaCodec_getOwnCodecInfo(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -2896,7 +2968,7 @@ static jobject android_media_MediaCodec_getOwnCodecInfo(
return codecInfoObj;
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
return NULL;
}
@@ -2908,7 +2980,8 @@ android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz)
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException",
+ GetExceptionMessage(codec, NULL).c_str());
return 0;
}
@@ -2937,7 +3010,7 @@ static void android_media_MediaCodec_setParameters(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -2948,7 +3021,7 @@ static void android_media_MediaCodec_setParameters(
err = codec->setParameters(params);
}
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, codec);
}
static void android_media_MediaCodec_setVideoScalingMode(
@@ -2956,13 +3029,14 @@ static void android_media_MediaCodec_setVideoScalingMode(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
&& mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ String8::format("Unrecognized mode: %d", mode));
return;
}
@@ -2974,7 +3048,7 @@ static void android_media_MediaCodec_setAudioPresentation(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
@@ -2986,14 +3060,14 @@ static jobject android_media_MediaCodec_getSupportedVendorParameters(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
jobject ret = NULL;
status_t status = codec->querySupportedVendorParameters(env, &ret);
if (status != OK) {
- throwExceptionAsNecessary(env, status);
+ throwExceptionAsNecessary(env, status, codec);
}
return ret;
@@ -3004,7 +3078,7 @@ static jobject android_media_MediaCodec_getParameterDescriptor(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return NULL;
}
@@ -3021,13 +3095,13 @@ static void android_media_MediaCodec_subscribeToVendorParameters(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t status = codec->subscribeToVendorParameters(env, names);
if (status != OK) {
- throwExceptionAsNecessary(env, status);
+ throwExceptionAsNecessary(env, status, codec);
}
return;
}
@@ -3037,13 +3111,13 @@ static void android_media_MediaCodec_unsubscribeFromVendorParameters(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL || codec->initCheck() != OK) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
return;
}
status_t status = codec->unsubscribeFromVendorParameters(env, names);
if (status != OK) {
- throwExceptionAsNecessary(env, status);
+ throwExceptionAsNecessary(env, status, codec);
}
return;
}
@@ -3440,11 +3514,15 @@ static jobject android_media_MediaCodec_LinearBlock_native_map(
if (!context->mReadonlyMapping) {
const C2BufferData data = buffer->data();
if (data.type() != C2BufferData::LINEAR) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ "Underlying buffer is not a linear buffer");
return nullptr;
}
if (data.linearBlocks().size() != 1u) {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ "Underlying buffer contains more than one block");
return nullptr;
}
C2ConstLinearBlock block = data.linearBlocks().front();
@@ -3492,7 +3570,9 @@ static jobject android_media_MediaCodec_LinearBlock_native_map(
false, // readOnly
true /* clearBuffer */);
}
- throwExceptionAsNecessary(env, INVALID_OPERATION);
+ throwExceptionAsNecessary(
+ env, INVALID_OPERATION, ACTION_CODE_FATAL,
+ "Underlying buffer is empty");
return nullptr;
}
@@ -3515,7 +3595,9 @@ static void PopulateNamesVector(
}
const char *cstr = env->GetStringUTFChars(jstr, nullptr);
if (cstr == nullptr) {
- throwExceptionAsNecessary(env, BAD_VALUE);
+ throwExceptionAsNecessary(
+ env, BAD_VALUE, ACTION_CODE_FATAL,
+ "Error converting Java string to native");
return;
}
names->emplace_back(cstr);
@@ -3567,6 +3649,7 @@ static jboolean android_media_MediaCodec_LinearBlock_checkCompatible(
}
status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible);
if (err != OK) {
+ // TODO: CodecErrorLog
throwExceptionAsNecessary(env, err);
}
return isCompatible;
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 616c31b29157..fbaf64fda572 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -176,6 +176,8 @@ struct JMediaCodec : public AHandler {
const sp<ICrypto> &getCrypto() { return mCrypto; }
+ std::string getExceptionMessage(const char *msg) const;
+
protected:
virtual ~JMediaCodec();
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index edc902e41e9a..53731f06816a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import com.android.compose.rememberSystemUiController
import com.android.credentialmanager.common.material.ModalBottomSheetLayout
import com.android.credentialmanager.common.material.ModalBottomSheetValue
import com.android.credentialmanager.common.material.rememberModalBottomSheetState
@@ -38,6 +39,12 @@ fun ModalBottomSheet(
initialValue = ModalBottomSheetValue.Expanded,
skipHalfExpanded = true
)
+ val sysUiController = rememberSystemUiController()
+ if (state.targetValue == ModalBottomSheetValue.Hidden) {
+ setTransparentSystemBarsColor(sysUiController)
+ } else {
+ setBottomSheetSystemBarsColor(sysUiController)
+ }
ModalBottomSheetLayout(
sheetBackgroundColor = LocalAndroidColorScheme.current.colorSurfaceBright,
modifier = Modifier.background(Color.Transparent),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
index dfff3d694877..244b604a87f9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
@@ -37,6 +37,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalAccessibilityManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.compose.rememberSystemUiController
import com.android.credentialmanager.R
import com.android.credentialmanager.common.material.Scrim
import com.android.credentialmanager.ui.theme.Shapes
@@ -49,6 +50,8 @@ fun Snackbar(
onDismiss: () -> Unit,
dismissOnTimeout: Boolean = false,
) {
+ val sysUiController = rememberSystemUiController()
+ setTransparentSystemBarsColor(sysUiController)
BoxWithConstraints {
Box(Modifier.fillMaxSize()) {
Scrim(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index ed4cc959543b..648d83268541 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -46,7 +46,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.rememberSystemUiController
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
import com.android.credentialmanager.common.BaseEntry
@@ -67,7 +66,6 @@ import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
import com.android.credentialmanager.common.ui.SheetContainerCard
import com.android.credentialmanager.common.ui.PasskeyBenefitRow
import com.android.credentialmanager.common.ui.HeadlineText
-import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
import com.android.credentialmanager.logging.CreateCredentialEvent
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -77,8 +75,6 @@ fun CreateCredentialScreen(
createCredentialUiState: CreateCredentialUiState,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
- val sysUiController = rememberSystemUiController()
- setBottomSheetSystemBarsColor(sysUiController)
ModalBottomSheet(
sheetContent = {
// Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index c27ac943bca7..6d7ecd70eb0b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -41,7 +41,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.rememberSystemUiController
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
import com.android.credentialmanager.common.BaseEntry
@@ -62,8 +61,6 @@ import com.android.credentialmanager.common.ui.CredentialListSectionHeader
import com.android.credentialmanager.common.ui.HeadlineIcon
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.Snackbar
-import com.android.credentialmanager.common.ui.setTransparentSystemBarsColor
-import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
import com.android.credentialmanager.logging.GetCredentialEvent
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -73,9 +70,7 @@ fun GetCredentialScreen(
getCredentialUiState: GetCredentialUiState,
providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
- val sysUiController = rememberSystemUiController()
if (getCredentialUiState.currentScreenState == GetScreenState.REMOTE_ONLY) {
- setTransparentSystemBarsColor(sysUiController)
RemoteCredentialSnackBarScreen(
onClick = viewModel::getFlowOnMoreOptionOnSnackBarSelected,
onCancel = viewModel::onUserCancel,
@@ -84,7 +79,6 @@ fun GetCredentialScreen(
viewModel.uiMetrics.log(GetCredentialEvent.CREDMAN_GET_CRED_SCREEN_REMOTE_ONLY)
} else if (getCredentialUiState.currentScreenState
== GetScreenState.UNLOCKED_AUTH_ENTRIES_ONLY) {
- setTransparentSystemBarsColor(sysUiController)
EmptyAuthEntrySnackBarScreen(
authenticationEntryList =
getCredentialUiState.providerDisplayInfo.authenticationEntryList,
@@ -95,7 +89,6 @@ fun GetCredentialScreen(
viewModel.uiMetrics.log(GetCredentialEvent
.CREDMAN_GET_CRED_SCREEN_UNLOCKED_AUTH_ENTRIES_ONLY)
} else {
- setBottomSheetSystemBarsColor(sysUiController)
ModalBottomSheet(
sheetContent = {
// Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 067efe97f6b6..e069a9ac776b 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -455,7 +455,8 @@ public class BugreportProgressService extends Service {
intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, nonce);
intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
- context.sendBroadcast(intent, android.Manifest.permission.DUMP);
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
+ android.Manifest.permission.DUMP);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
index 6a6e81e9cb46..14810d9baf02 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
@@ -30,7 +30,6 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -50,7 +49,7 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu
android.R.attr.textColorPrimary).getDefaultColor();
private int mPosition = 0;
private final PinShapeAdapter mPinShapeAdapter;
- private Animation mCurrentPlayingAnimation;
+ private ValueAnimator mValueAnimator = ValueAnimator.ofFloat(1f, 0f);
public PinShapeNonHintingView(Context context, AttributeSet attrs) {
super(context, attrs);
mPinShapeAdapter = new PinShapeAdapter(context);
@@ -80,15 +79,17 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu
Log.e(getClass().getName(), "Trying to delete a non-existent char");
return;
}
+ if (mValueAnimator.isRunning()) {
+ mValueAnimator.end();
+ }
mPosition--;
ImageView pinDot = (ImageView) getChildAt(mPosition);
- ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
- animator.addUpdateListener(valueAnimator -> {
+ mValueAnimator.addUpdateListener(valueAnimator -> {
float value = (float) valueAnimator.getAnimatedValue();
pinDot.setScaleX(value);
pinDot.setScaleY(value);
});
- animator.addListener(new AnimatorListenerAdapter() {
+ mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
@@ -96,11 +97,10 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu
PinShapeNonHintingView.this,
new PinShapeViewTransition());
removeView(pinDot);
- mCurrentPlayingAnimation = null;
}
});
- animator.setDuration(PasswordTextView.DISAPPEAR_DURATION);
- animator.start();
+ mValueAnimator.setDuration(PasswordTextView.DISAPPEAR_DURATION);
+ mValueAnimator.start();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 868ffcf54fe1..e0b9f01bf662 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -34,6 +34,7 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
@@ -43,7 +44,6 @@ import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -52,7 +52,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
-/***
+/**
* Controls two ripple effects:
* 1. Unlocked ripple: shows when authentication is successful
* 2. UDFPS dwell ripple: shows when the user has their finger down on the UDFPS area and reacts
@@ -71,14 +71,15 @@ class AuthRippleController @Inject constructor(
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController,
- private val bypassController: KeyguardBypassController,
- private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
private val featureFlags: FeatureFlags,
private val logger: KeyguardLogger,
- rippleView: AuthRippleView?
-) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
+ private val biometricUnlockController: BiometricUnlockController,
+ rippleView: AuthRippleView?
+) :
+ ViewController<AuthRippleView>(rippleView),
+ KeyguardStateController.Callback,
WakefulnessLifecycle.Observer {
@VisibleForTesting
@@ -102,8 +103,24 @@ class AuthRippleController @Inject constructor(
keyguardStateController.addCallback(this)
wakefulnessLifecycle.addObserver(this)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
+ biometricUnlockController.addListener(biometricModeListener)
}
+ private val biometricModeListener =
+ object : BiometricUnlockController.BiometricUnlockEventsListener {
+ override fun onBiometricUnlockedWithKeyguardDismissal(
+ biometricSourceType: BiometricSourceType?
+ ) {
+ if (biometricSourceType != null) {
+ showUnlockRipple(biometricSourceType)
+ } else {
+ logger.log(TAG,
+ LogLevel.ERROR,
+ "Unexpected scenario where biometricSourceType is null")
+ }
+ }
+ }
+
@VisibleForTesting
public override fun onViewDetached() {
udfpsController?.removeCallback(udfpsControllerCallback)
@@ -113,6 +130,7 @@ class AuthRippleController @Inject constructor(
keyguardStateController.removeCallback(this)
wakefulnessLifecycle.removeObserver(this)
commandRegistry.unregisterCommand("auth-ripple")
+ biometricUnlockController.removeListener(biometricModeListener)
notificationShadeWindowController.setForcePluginOpen(false, this)
}
@@ -143,9 +161,6 @@ class AuthRippleController @Inject constructor(
showUnlockedRipple()
}
} else if (biometricSourceType == BiometricSourceType.FACE) {
- if (!bypassController.canBypass() && !authController.isUdfpsFingerDown) {
- return
- }
faceSensorLocation?.let {
mView.setSensorLocation(it)
circleReveal = CircleReveal(
@@ -267,7 +282,6 @@ class AuthRippleController @Inject constructor(
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.fadeDwellRipple()
}
- showUnlockRipple(biometricSourceType)
}
override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a7b45f4faee3..0bc8506f272d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -285,7 +285,7 @@ object Flags {
/** Enables Font Scaling Quick Settings tile */
// TODO(b/269341316): Tracking Bug
@JvmField
- val ENABLE_FONT_SCALING_TILE = unreleasedFlag(509, "enable_font_scaling_tile", teamfood = true)
+ val ENABLE_FONT_SCALING_TILE = releasedFlag(509, "enable_font_scaling_tile")
/** Enables new QS Edit Mode visual refresh */
// TODO(b/269787742): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 6bc9837e8751..3567d814f63e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -471,7 +471,7 @@ constructor(
}
val callback =
- object : BiometricUnlockController.BiometricModeListener {
+ object : BiometricUnlockController.BiometricUnlockEventsListener {
override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
dispatchUpdate()
}
@@ -481,10 +481,10 @@ constructor(
}
}
- biometricUnlockController.addBiometricModeListener(callback)
+ biometricUnlockController.addListener(callback)
dispatchUpdate()
- awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ awaitClose { biometricUnlockController.removeListener(callback) }
}
override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index cf5ecdddf854..6742e4f3041e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -163,7 +163,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private PendingAuthenticated mPendingAuthenticated = null;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
- private Set<BiometricModeListener> mBiometricModeListeners = new HashSet<>();
+ private Set<BiometricUnlockEventsListener> mBiometricUnlockEventsListeners = new HashSet<>();
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
@@ -314,14 +314,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mKeyguardViewController = keyguardViewController;
}
- /** Adds a {@link BiometricModeListener}. */
- public void addBiometricModeListener(BiometricModeListener listener) {
- mBiometricModeListeners.add(listener);
+ /** Adds a {@link BiometricUnlockEventsListener}. */
+ public void addListener(BiometricUnlockEventsListener listener) {
+ mBiometricUnlockEventsListeners.add(listener);
}
- /** Removes a {@link BiometricModeListener}. */
- public void removeBiometricModeListener(BiometricModeListener listener) {
- mBiometricModeListeners.remove(listener);
+ /** Removes a {@link BiometricUnlockEventsListener}. */
+ public void removeListener(BiometricUnlockEventsListener listener) {
+ mBiometricUnlockEventsListeners.remove(listener);
}
private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
@@ -387,7 +387,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
@Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
- Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
+ Trace.beginSection("BiometricUnlockController#onBiometricUnlocked");
if (mUpdateMonitor.isGoingToSleep()) {
mLogger.deferringAuthenticationDueToSleep(userId,
biometricSourceType,
@@ -411,10 +411,15 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mKeyguardViewMediator.userActivity();
startWakeAndUnlock(biometricSourceType, isStrongBiometric);
} else {
- mLogger.d("onBiometricAuthenticated aborted by bypass controller");
+ mLogger.d("onBiometricUnlocked aborted by bypass controller");
}
}
+ /**
+ * Wake and unlock the device in response to successful authentication using biometrics.
+ * @param biometricSourceType Biometric source that was used to authenticate.
+ * @param isStrongBiometric
+ */
public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
int mode = calculateMode(biometricSourceType, isStrongBiometric);
@@ -422,6 +427,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
|| mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
|| mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
vibrateSuccess(biometricSourceType);
+ onBiometricUnlockedWithKeyguardDismissal(biometricSourceType);
}
startWakeAndUnlock(mode);
}
@@ -502,11 +508,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
private void onModeChanged(@WakeAndUnlockMode int mode) {
- for (BiometricModeListener listener : mBiometricModeListeners) {
+ for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onModeChanged(mode);
}
}
+ private void onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType biometricSourceType) {
+ for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
+ listener.onBiometricUnlockedWithKeyguardDismissal(biometricSourceType);
+ }
+ }
+
public boolean hasPendingAuthentication() {
return mPendingAuthenticated != null
&& mUpdateMonitor
@@ -777,7 +789,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mMode = MODE_NONE;
mBiometricType = null;
mNotificationShadeWindowController.setForceDozeBrightness(false);
- for (BiometricModeListener listener : mBiometricModeListeners) {
+ for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onResetMode();
}
mNumConsecutiveFpFailures = 0;
@@ -895,10 +907,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
/** An interface to interact with the {@link BiometricUnlockController}. */
- public interface BiometricModeListener {
+ public interface BiometricUnlockEventsListener {
/** Called when {@code mMode} is reset to {@link #MODE_NONE}. */
default void onResetMode() {}
/** Called when {@code mMode} has changed in {@link #startWakeAndUnlock(int)}. */
default void onModeChanged(@WakeAndUnlockMode int mode) {}
+
+ /**
+ * Called when the device is unlocked successfully using biometrics with the keyguard also
+ * being dismissed.
+ */
+ default void onBiometricUnlockedWithKeyguardDismissal(
+ BiometricSourceType biometricSourceType) { }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index aabe0cb9794c..0c8e9e56b04a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1675,8 +1675,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mStatusBarStateController.addCallback(mStateListener,
SysuiStatusBarStateController.RANK_STATUS_BAR);
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
- mBiometricUnlockController.addBiometricModeListener(
- new BiometricUnlockController.BiometricModeListener() {
+ mBiometricUnlockController.addListener(
+ new BiometricUnlockController.BiometricUnlockEventsListener() {
@Override
public void onResetMode() {
setWakeAndUnlocking(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 30d2295206d8..a8a834f1e8f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -558,8 +558,10 @@ public interface StatusBarIconController {
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
+ Context mobileContext = mMobileContextProvider
+ .getMobileContextForSub(subId, mContext);
mDemoStatusIcons.addModernMobileView(
- mContext,
+ mobileContext,
mMobileIconsViewModel.getLogger(),
subId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 90c32dc08045..3a11635f75c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -40,6 +40,9 @@ interface MobileConnectionRepository {
/** The subscriptionId that this connection represents */
val subId: Int
+ /** The carrierId for this connection. See [TelephonyManager.getSimCarrierId] */
+ val carrierId: StateFlow<Int>
+
/**
* The table log buffer created for this connection. Will have the name "MobileConnectionLog
* [subId]"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 809772eec2f0..6b86432b8171 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.telephony.CellSignalStrength
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
@@ -25,6 +26,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameMode
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CARRIER_ID
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CARRIER_NETWORK_CHANGE
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CDMA_LEVEL
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_EMERGENCY
@@ -52,6 +54,17 @@ class DemoMobileConnectionRepository(
override val tableLogBuffer: TableLogBuffer,
val scope: CoroutineScope,
) : MobileConnectionRepository {
+ private val _carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
+ override val carrierId =
+ _carrierId
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = COL_CARRIER_ID,
+ _carrierId.value,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierId.value)
+
private val _isEmergencyOnly = MutableStateFlow(false)
override val isEmergencyOnly =
_isEmergencyOnly
@@ -186,6 +199,8 @@ class DemoMobileConnectionRepository(
dataEnabled.value = true
networkName.value = NetworkNameModel.IntentDerived(event.name)
+ _carrierId.value = event.carrierId ?: INVALID_SUBSCRIPTION_ID
+
cdmaRoaming.value = event.roaming
_isRoaming.value = event.roaming
// TODO(b/261029387): not yet supported
@@ -208,6 +223,8 @@ class DemoMobileConnectionRepository(
// This is always true here, because we split out disabled states at the data-source level
dataEnabled.value = true
networkName.value = NetworkNameModel.IntentDerived(CARRIER_MERGED_NAME)
+ // TODO(b/276943904): is carrierId a thing with carrier merged networks?
+ _carrierId.value = INVALID_SUBSCRIPTION_ID
numberOfLevels.value = event.numberOfLevels
cdmaRoaming.value = false
_primaryLevel.value = event.level
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 94d6d0b1db44..a609917351d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
@@ -157,6 +158,7 @@ class CarrierMergedConnectionRepository(
.stateIn(scope, SharingStarted.WhileSubscribed(), DataConnectionState.Disconnected)
override val isRoaming = MutableStateFlow(false).asStateFlow()
+ override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
override val isInService = MutableStateFlow(true).asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index b3737ecd1e0b..8869dfe02697 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -109,6 +109,11 @@ class FullMobileConnectionRepository(
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
+ override val carrierId =
+ activeRepo
+ .flatMapLatest { it.carrierId }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierId.value)
+
override val cdmaRoaming =
activeRepo
.flatMapLatest { it.cdmaRoaming }
@@ -321,13 +326,14 @@ class FullMobileConnectionRepository(
}
companion object {
+ const val COL_CARRIER_ID = "carrierId"
+ const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChangeActive"
+ const val COL_CDMA_LEVEL = "cdmaLevel"
const val COL_EMERGENCY = "emergencyOnly"
- const val COL_ROAMING = "roaming"
- const val COL_OPERATOR = "operatorName"
- const val COL_IS_IN_SERVICE = "isInService"
const val COL_IS_GSM = "isGsm"
- const val COL_CDMA_LEVEL = "cdmaLevel"
+ const val COL_IS_IN_SERVICE = "isInService"
+ const val COL_OPERATOR = "operatorName"
const val COL_PRIMARY_LEVEL = "primaryLevel"
- const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChangeActive"
+ const val COL_ROAMING = "roaming"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index d0c6215a55d8..b475183d98c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
-import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
import android.telephony.CellSignalStrengthCdma
@@ -31,6 +31,7 @@ import android.telephony.TelephonyManager.ERI_FLASH
import android.telephony.TelephonyManager.ERI_ON
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
@@ -65,6 +66,7 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
@@ -75,7 +77,6 @@ import kotlinx.coroutines.flow.stateIn
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
- private val context: Context,
override val subId: Int,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
@@ -293,6 +294,23 @@ class MobileConnectionRepositoryImpl(
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val carrierId =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED),
+ map = { intent, _ -> intent },
+ )
+ .filter { intent ->
+ intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
+ }
+ .map { it.carrierId() }
+ .onStart {
+ // Make sure we get the initial carrierId
+ emit(telephonyManager.simCarrierId)
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
+
override val networkName: StateFlow<NetworkNameModel> =
broadcastDispatcher
.broadcastFlow(
@@ -317,7 +335,6 @@ class MobileConnectionRepositoryImpl(
@Inject
constructor(
private val broadcastDispatcher: BroadcastDispatcher,
- private val context: Context,
private val telephonyManager: TelephonyManager,
private val logger: MobileInputLogger,
private val carrierConfigRepository: CarrierConfigRepository,
@@ -332,7 +349,6 @@ class MobileConnectionRepositoryImpl(
networkNameSeparator: String,
): MobileConnectionRepository {
return MobileConnectionRepositoryImpl(
- context,
subId,
defaultNetworkName,
networkNameSeparator,
@@ -349,6 +365,9 @@ class MobileConnectionRepositoryImpl(
}
}
+private fun Intent.carrierId(): Int =
+ getIntExtra(TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID)
+
/**
* Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
* shared flow and then split them back out into other flows.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 22351f8b2821..b36ba3845fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -16,15 +16,21 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+import android.content.Context
import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
-import com.android.settingslib.mobile.TelephonyIcons.NOT_DEFAULT_DATA
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.DefaultIcon
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.OverriddenIcon
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,8 +40,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
interface MobileIconInteractor {
@@ -76,7 +80,7 @@ interface MobileIconInteractor {
val alwaysUseCdmaLevel: StateFlow<Boolean>
/** Observable for RAT type (network type) indicator */
- val networkTypeIconGroup: StateFlow<MobileIconGroup>
+ val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
/**
* Provider name for this network connection. The name can be one of 3 values:
@@ -119,10 +123,11 @@ class MobileIconInteractorImpl(
override val mobileIsDefault: StateFlow<Boolean>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
- defaultDataSubId: StateFlow<Int>,
override val isDefaultConnectionFailed: StateFlow<Boolean>,
override val isForceHidden: Flow<Boolean>,
connectionRepository: MobileConnectionRepository,
+ private val context: Context,
+ val carrierIdOverrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
) : MobileIconInteractor {
override val tableLogBuffer: TableLogBuffer = connectionRepository.tableLogBuffer
@@ -130,14 +135,14 @@ class MobileIconInteractorImpl(
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
- private val isDefault =
- defaultDataSubId
- .mapLatest { connectionRepository.subId == it }
- .stateIn(
- scope,
- SharingStarted.WhileSubscribed(),
- connectionRepository.subId == defaultDataSubId.value
- )
+ // True if there exists _any_ icon override for this carrierId. Note that overrides can include
+ // any or none of the icon groups defined in MobileMappings, so we still need to check on a
+ // per-network-type basis whether or not the given icon group is overridden
+ private val carrierIdIconOverrideExists =
+ connectionRepository.carrierId
+ .map { carrierIdOverrides.carrierIdEntryExists(it) }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
@@ -157,36 +162,57 @@ class MobileIconInteractorImpl(
connectionRepository.networkName.value
)
- /** Observable for the current RAT indicator icon ([MobileIconGroup]) */
- override val networkTypeIconGroup: StateFlow<MobileIconGroup> =
+ /** What the mobile icon would be before carrierId overrides */
+ private val defaultNetworkType: StateFlow<MobileIconGroup> =
combine(
connectionRepository.resolvedNetworkType,
defaultMobileIconMapping,
defaultMobileIconGroup,
- isDefault,
- ) { resolvedNetworkType, mapping, defaultGroup, isDefault ->
- if (!isDefault) {
- return@combine NOT_DEFAULT_DATA
- }
-
+ ) { resolvedNetworkType, mapping, defaultGroup ->
when (resolvedNetworkType) {
is ResolvedNetworkType.CarrierMergedNetworkType ->
resolvedNetworkType.iconGroupOverride
- else -> mapping[resolvedNetworkType.lookupKey] ?: defaultGroup
+ else -> {
+ mapping[resolvedNetworkType.lookupKey] ?: defaultGroup
+ }
}
}
- .distinctUntilChanged()
- .onEach {
- // Doesn't use [logDiffsForTable] because [MobileIconGroup] can't implement the
- // [Diffable] interface.
- tableLogBuffer.logChange(
- prefix = "",
- columnName = "networkTypeIcon",
- value = it.name
- )
- }
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
+ override val networkTypeIconGroup =
+ combine(
+ defaultNetworkType,
+ carrierIdIconOverrideExists,
+ ) { networkType, overrideExists ->
+ // DefaultIcon comes out of the icongroup lookup, we check for overrides here
+ if (overrideExists) {
+ val iconOverride =
+ carrierIdOverrides.getOverrideFor(
+ connectionRepository.carrierId.value,
+ networkType.name,
+ context.resources,
+ )
+ if (iconOverride > 0) {
+ OverriddenIcon(networkType, iconOverride)
+ } else {
+ DefaultIcon(networkType)
+ }
+ } else {
+ DefaultIcon(networkType)
+ }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ initialValue = DefaultIcon(defaultMobileIconGroup.value),
+ )
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ DefaultIcon(defaultMobileIconGroup.value),
+ )
+
override val isEmergencyOnly = connectionRepository.isEmergencyOnly
override val isRoaming: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 6c8310ac3d29..1e3122b1a515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+import android.content.Context
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import com.android.settingslib.SignalIcon.MobileIconGroup
@@ -75,9 +76,6 @@ interface MobileIconsInteractor {
/** True if the CDMA level should be preferred over the primary level. */
val alwaysUseCdmaLevel: StateFlow<Boolean>
- /** Tracks the subscriptionId set as the default for data connections */
- val defaultDataSubId: StateFlow<Int>
-
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
@@ -112,6 +110,7 @@ constructor(
connectivityRepository: ConnectivityRepository,
userSetupRepo: UserSetupRepository,
@Application private val scope: CoroutineScope,
+ private val context: Context,
) : MobileIconsInteractor {
override val mobileIsDefault = mobileConnectionsRepo.mobileIsDefault
@@ -184,8 +183,6 @@ constructor(
)
.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
- override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
-
/**
* Copied from the old pipeline. We maintain a 2s period of time where we will keep the
* validated bit from the old active network (A) while data is changing to the new one (B).
@@ -282,10 +279,10 @@ constructor(
mobileIsDefault,
defaultMobileIconMapping,
defaultMobileIconGroup,
- defaultDataSubId,
isDefaultConnectionFailed,
isForceHidden,
mobileConnectionsRepo.getRepoForSubId(subId),
+ context,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/NetworkTypeIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/NetworkTypeIconModel.kt
new file mode 100644
index 000000000000..6ea5f90ddc71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/NetworkTypeIconModel.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.domain.model
+
+import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
+/**
+ * A data wrapper class for [MobileIconGroup]. One lingering nuance of this pipeline is its
+ * dependency on MobileMappings for its lookup from NetworkType -> NetworkTypeIcon. And because
+ * MobileMappings is a static map of (netType, icon) that knows nothing of `carrierId`, we need the
+ * concept of a "default" or "overridden" icon type.
+ *
+ * Until we can remove that dependency on MobileMappings, we should just allow for the composition
+ * of overriding an icon id using the lookup defined in [MobileIconCarrierIdOverrides]. By using the
+ * [overrideIcon] method defined below, we can create any arbitrarily overridden network type icon.
+ */
+sealed interface NetworkTypeIconModel : Diffable<NetworkTypeIconModel> {
+ val contentDescription: Int
+ val iconId: Int
+ val name: String
+
+ data class DefaultIcon(
+ val iconGroup: MobileIconGroup,
+ ) : NetworkTypeIconModel {
+ override val contentDescription = iconGroup.dataContentDescription
+ override val iconId = iconGroup.dataType
+ override val name = iconGroup.name
+
+ override fun logDiffs(prevVal: NetworkTypeIconModel, row: TableRowLogger) {
+ if (prevVal !is DefaultIcon || prevVal.name != name) {
+ row.logChange(COL_NETWORK_ICON, name)
+ }
+ }
+ }
+
+ data class OverriddenIcon(
+ val iconGroup: MobileIconGroup,
+ override val iconId: Int,
+ ) : NetworkTypeIconModel {
+ override val contentDescription = iconGroup.dataContentDescription
+ override val name = iconGroup.name
+
+ override fun logDiffs(prevVal: NetworkTypeIconModel, row: TableRowLogger) {
+ if (prevVal !is OverriddenIcon || prevVal.name != name || prevVal.iconId != iconId) {
+ row.logChange(COL_NETWORK_ICON, "Ovrd($name)")
+ }
+ }
+ }
+
+ companion object {
+ const val COL_NETWORK_ICON = "networkTypeIcon"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 62bc27f9dd4e..dce7bf21fadd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -146,11 +146,10 @@ constructor(
combine(
iconInteractor.isDataConnected,
iconInteractor.isDataEnabled,
- iconInteractor.isDefaultConnectionFailed,
iconInteractor.alwaysShowDataRatIcon,
iconInteractor.mobileIsDefault,
- ) { dataConnected, dataEnabled, failedConnection, alwaysShow, mobileIsDefault ->
- alwaysShow || (dataConnected && dataEnabled && !failedConnection && mobileIsDefault)
+ ) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault ->
+ alwaysShow || (dataEnabled && dataConnected && mobileIsDefault)
}
.distinctUntilChanged()
.logDiffsForTable(
@@ -167,12 +166,12 @@ constructor(
showNetworkTypeIcon,
) { networkTypeIconGroup, shouldShow ->
val desc =
- if (networkTypeIconGroup.dataContentDescription != 0)
- ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
+ if (networkTypeIconGroup.contentDescription != 0)
+ ContentDescription.Resource(networkTypeIconGroup.contentDescription)
else null
val icon =
- if (networkTypeIconGroup.dataType != 0)
- Icon.Resource(networkTypeIconGroup.dataType, desc)
+ if (networkTypeIconGroup.iconId != 0)
+ Icon.Resource(networkTypeIconGroup.iconId, desc)
else null
return@combine when {
!shouldShow -> null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index a245c01d74de..7d9ccb642e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
@@ -50,6 +49,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
@@ -73,16 +73,28 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock private lateinit var bypassController: KeyguardBypassController
- @Mock private lateinit var biometricUnlockController: BiometricUnlockController
- @Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
- @Mock private lateinit var udfpsController: UdfpsController
- @Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock private lateinit var featureFlags: FeatureFlags
- @Mock private lateinit var lightRevealScrim: LightRevealScrim
- @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
+ @Mock
+ private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock
+ private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock
+ private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock
+ private lateinit var udfpsControllerProvider: Provider<UdfpsController>
+ @Mock
+ private lateinit var udfpsController: UdfpsController
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var lightRevealScrim: LightRevealScrim
+ @Mock
+ private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
+
+ @Captor
+ private lateinit var biometricUnlockListener:
+ ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
@Before
fun setUp() {
@@ -106,13 +118,12 @@ class AuthRippleControllerTest : SysuiTestCase() {
wakefulnessLifecycle,
commandRegistry,
notificationShadeWindowController,
- bypassController,
- biometricUnlockController,
udfpsControllerProvider,
statusBarStateController,
featureFlags,
KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
- rippleView
+ biometricUnlockController,
+ rippleView,
)
controller.init()
`when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim)
@@ -134,12 +145,9 @@ class AuthRippleControllerTest : SysuiTestCase() {
eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
// WHEN fingerprint authenticated
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
+ verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
+ biometricUnlockListener.value
+ .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
// THEN update sensor location and show ripple
verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
@@ -191,51 +199,6 @@ class AuthRippleControllerTest : SysuiTestCase() {
}
@Test
- fun testFaceTriggerBypassEnabled_Ripple() {
- // GIVEN face auth sensor exists, keyguard is showing & unlocking with face is allowed
- val faceLocation = Point(5, 5)
- `when`(authController.faceSensorLocation).thenReturn(faceLocation)
- controller.onViewAttached()
-
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- BiometricSourceType.FACE)).thenReturn(true)
-
- // WHEN bypass is enabled & face authenticated
- `when`(bypassController.canBypass()).thenReturn(true)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FACE /* type */,
- false /* isStrongBiometric */)
-
- // THEN show ripple
- verify(rippleView).setSensorLocation(faceLocation)
- verify(rippleView).startUnlockedRipple(any())
- }
-
- @Test
- fun testFaceTriggerNonBypass_NoRipple() {
- // GIVEN face auth sensor exists
- val faceLocation = Point(5, 5)
- `when`(authController.faceSensorLocation).thenReturn(faceLocation)
- controller.onViewAttached()
-
- // WHEN bypass isn't enabled & face authenticated
- `when`(bypassController.canBypass()).thenReturn(false)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FACE /* type */,
- false /* isStrongBiometric */)
-
- // THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
fun testNullFaceSensorLocationDoesNothing() {
`when`(authController.faceSensorLocation).thenReturn(null)
controller.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index ce17167f587d..3fd97da23157 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -461,8 +461,8 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
runCurrent()
- val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
- verify(biometricUnlockController).addBiometricModeListener(captor.capture())
+ val captor = argumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>()
+ verify(biometricUnlockController).addListener(captor.capture())
listOf(
BiometricUnlockController.MODE_NONE,
@@ -498,7 +498,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
runCurrent()
- verify(biometricUnlockController).removeBiometricModeListener(captor.value)
+ verify(biometricUnlockController).removeListener(captor.value)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 4bb14a1eba0d..ba91d87c659a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -71,241 +71,241 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class HeadsUpCoordinatorTest : SysuiTestCase() {
- private lateinit var mCoordinator: HeadsUpCoordinator
+ private lateinit var coordinator: HeadsUpCoordinator
// captured listeners and pluggables:
- private lateinit var mCollectionListener: NotifCollectionListener
- private lateinit var mNotifPromoter: NotifPromoter
- private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
- private lateinit var mBeforeTransformGroupsListener: OnBeforeTransformGroupsListener
- private lateinit var mBeforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
- private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
- private lateinit var mNotifSectioner: NotifSectioner
- private lateinit var mActionPressListener: Consumer<NotificationEntry>
-
- private val mNotifPipeline: NotifPipeline = mock()
- private val mLogger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
- private val mHeadsUpManager: HeadsUpManager = mock()
- private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
- private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
- private val mRemoteInputManager: NotificationRemoteInputManager = mock()
- private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
- private val mHeaderController: NodeController = mock()
- private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider = mock()
- private val mFlags: NotifPipelineFlags = mock()
-
- private lateinit var mEntry: NotificationEntry
- private lateinit var mGroupSummary: NotificationEntry
- private lateinit var mGroupPriority: NotificationEntry
- private lateinit var mGroupSibling1: NotificationEntry
- private lateinit var mGroupSibling2: NotificationEntry
- private lateinit var mGroupChild1: NotificationEntry
- private lateinit var mGroupChild2: NotificationEntry
- private lateinit var mGroupChild3: NotificationEntry
- private val mSystemClock = FakeSystemClock()
- private val mExecutor = FakeExecutor(mSystemClock)
- private val mHuns: ArrayList<NotificationEntry> = ArrayList()
- private lateinit var mHelper: NotificationGroupTestHelper
+ private lateinit var collectionListener: NotifCollectionListener
+ private lateinit var notifPromoter: NotifPromoter
+ private lateinit var notifLifetimeExtender: NotifLifetimeExtender
+ private lateinit var beforeTransformGroupsListener: OnBeforeTransformGroupsListener
+ private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
+ private lateinit var onHeadsUpChangedListener: OnHeadsUpChangedListener
+ private lateinit var notifSectioner: NotifSectioner
+ private lateinit var actionPressListener: Consumer<NotificationEntry>
+
+ private val notifPipeline: NotifPipeline = mock()
+ private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
+ private val headsUpManager: HeadsUpManager = mock()
+ private val headsUpViewBinder: HeadsUpViewBinder = mock()
+ private val notificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
+ private val remoteInputManager: NotificationRemoteInputManager = mock()
+ private val endLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
+ private val headerController: NodeController = mock()
+ private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider = mock()
+ private val flags: NotifPipelineFlags = mock()
+
+ private lateinit var entry: NotificationEntry
+ private lateinit var groupSummary: NotificationEntry
+ private lateinit var groupPriority: NotificationEntry
+ private lateinit var groupSibling1: NotificationEntry
+ private lateinit var groupSibling2: NotificationEntry
+ private lateinit var groupChild1: NotificationEntry
+ private lateinit var groupChild2: NotificationEntry
+ private lateinit var groupChild3: NotificationEntry
+ private val systemClock = FakeSystemClock()
+ private val executor = FakeExecutor(systemClock)
+ private val huns: ArrayList<NotificationEntry> = ArrayList()
+ private lateinit var helper: NotificationGroupTestHelper
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mHelper = NotificationGroupTestHelper(mContext)
- mCoordinator = HeadsUpCoordinator(
- mLogger,
- mSystemClock,
- mHeadsUpManager,
- mHeadsUpViewBinder,
- mNotificationInterruptStateProvider,
- mRemoteInputManager,
- mLaunchFullScreenIntentProvider,
- mFlags,
- mHeaderController,
- mExecutor)
- mCoordinator.attach(mNotifPipeline)
+ helper = NotificationGroupTestHelper(mContext)
+ coordinator = HeadsUpCoordinator(
+ logger,
+ systemClock,
+ headsUpManager,
+ headsUpViewBinder,
+ notificationInterruptStateProvider,
+ remoteInputManager,
+ launchFullScreenIntentProvider,
+ flags,
+ headerController,
+ executor)
+ coordinator.attach(notifPipeline)
// capture arguments:
- mCollectionListener = withArgCaptor {
- verify(mNotifPipeline).addCollectionListener(capture())
+ collectionListener = withArgCaptor {
+ verify(notifPipeline).addCollectionListener(capture())
}
- mNotifPromoter = withArgCaptor {
- verify(mNotifPipeline).addPromoter(capture())
+ notifPromoter = withArgCaptor {
+ verify(notifPipeline).addPromoter(capture())
}
- mNotifLifetimeExtender = withArgCaptor {
- verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
+ notifLifetimeExtender = withArgCaptor {
+ verify(notifPipeline).addNotificationLifetimeExtender(capture())
}
- mBeforeTransformGroupsListener = withArgCaptor {
- verify(mNotifPipeline).addOnBeforeTransformGroupsListener(capture())
+ beforeTransformGroupsListener = withArgCaptor {
+ verify(notifPipeline).addOnBeforeTransformGroupsListener(capture())
}
- mBeforeFinalizeFilterListener = withArgCaptor {
- verify(mNotifPipeline).addOnBeforeFinalizeFilterListener(capture())
+ beforeFinalizeFilterListener = withArgCaptor {
+ verify(notifPipeline).addOnBeforeFinalizeFilterListener(capture())
}
- mOnHeadsUpChangedListener = withArgCaptor {
- verify(mHeadsUpManager).addListener(capture())
+ onHeadsUpChangedListener = withArgCaptor {
+ verify(headsUpManager).addListener(capture())
}
- mActionPressListener = withArgCaptor {
- verify(mRemoteInputManager).addActionPressListener(capture())
+ actionPressListener = withArgCaptor {
+ verify(remoteInputManager).addActionPressListener(capture())
}
- given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() }
- given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation ->
+ given(headsUpManager.allEntries).willAnswer { huns.stream() }
+ given(headsUpManager.isAlerting(anyString())).willAnswer { invocation ->
val key = invocation.getArgument<String>(0)
- mHuns.any { entry -> entry.key == key }
+ huns.any { entry -> entry.key == key }
}
- given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer { invocation ->
+ given(headsUpManager.canRemoveImmediately(anyString())).willAnswer { invocation ->
val key = invocation.getArgument<String>(0)
- !mHuns.any { entry -> entry.key == key }
+ !huns.any { entry -> entry.key == key }
}
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- mNotifSectioner = mCoordinator.sectioner
- mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
- mEntry = NotificationEntryBuilder().build()
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ notifSectioner = coordinator.sectioner
+ notifLifetimeExtender.setCallback(endLifetimeExtension)
+ entry = NotificationEntryBuilder().build()
// Same summary we can use for either set of children
- mGroupSummary = mHelper.createSummaryNotification(GROUP_ALERT_ALL, 0, "summary", 500)
+ groupSummary = helper.createSummaryNotification(GROUP_ALERT_ALL, 0, "summary", 500)
// One set of children with GROUP_ALERT_SUMMARY
- mGroupPriority = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 0, "priority", 400)
- mGroupSibling1 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 1, "sibling", 300)
- mGroupSibling2 = mHelper.createChildNotification(GROUP_ALERT_SUMMARY, 2, "sibling", 200)
+ groupPriority = helper.createChildNotification(GROUP_ALERT_SUMMARY, 0, "priority", 400)
+ groupSibling1 = helper.createChildNotification(GROUP_ALERT_SUMMARY, 1, "sibling", 300)
+ groupSibling2 = helper.createChildNotification(GROUP_ALERT_SUMMARY, 2, "sibling", 200)
// Another set of children with GROUP_ALERT_ALL
- mGroupChild1 = mHelper.createChildNotification(GROUP_ALERT_ALL, 1, "child", 350)
- mGroupChild2 = mHelper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250)
- mGroupChild3 = mHelper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150)
+ groupChild1 = helper.createChildNotification(GROUP_ALERT_ALL, 1, "child", 350)
+ groupChild2 = helper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250)
+ groupChild3 = helper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150)
// Set the default FSI decision
setShouldFullScreen(any(), FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
// Run tests with default feature flag state
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(Flags.FSI_ON_DND_UPDATE.default)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(Flags.FSI_ON_DND_UPDATE.default)
}
@Test
fun testCancelStickyNotification() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
- addHUN(mEntry)
- whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(true))
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+ whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
+ verify(headsUpManager, times(1)).removeNotification(anyString(), eq(true))
}
@Test
fun testCancelAndReAddStickyNotification() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
- addHUN(mEntry)
- whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true, false)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- addHUN(mEntry)
- assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+ whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true, false)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ addHUN(entry)
+ assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
}
@Test
fun hunNotRemovedWhenExtensionCancelled() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
- addHUN(mEntry)
- whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- mNotifLifetimeExtender.cancelLifetimeExtension(mEntry)
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), any())
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+ whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ notifLifetimeExtender.cancelLifetimeExtension(entry)
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(headsUpManager, times(0)).removeNotification(anyString(), any())
}
@Test
fun hunExtensionCancelledWhenHunActionPressed() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
- addHUN(mEntry)
- whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- mActionPressListener.accept(mEntry)
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- verify(mHeadsUpManager, times(1)).removeNotification(eq(mEntry.key), eq(true))
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+ whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ actionPressListener.accept(entry)
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(headsUpManager, times(1)).removeNotification(eq(entry.key), eq(true))
}
@Test
fun testCancelUpdatedStickyNotification() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
- addHUN(mEntry)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- addHUN(mEntry)
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ addHUN(entry)
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
}
@Test
fun testCancelNotification() {
- whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(false)
- addHUN(mEntry)
- whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
- mExecutor.advanceClockToLast()
- mExecutor.runAllReady()
- verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(false))
- verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(false)
+ addHUN(entry)
+ whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(headsUpManager, times(1)).removeNotification(anyString(), eq(false))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
}
@Test
fun testOnEntryAdded_shouldFullScreen() {
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
- mCollectionListener.onEntryAdded(mEntry)
- verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
+ collectionListener.onEntryAdded(entry)
+ verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
}
@Test
fun testOnEntryAdded_shouldNotFullScreen() {
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
- mCollectionListener.onEntryAdded(mEntry)
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
+ collectionListener.onEntryAdded(entry)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
}
@Test
fun testPromotesAddedHUN() {
// GIVEN the current entry should heads up
- whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+ setShouldHeadsUp(entry, true)
// WHEN the notification is added but not yet binding
- mCollectionListener.onEntryAdded(mEntry)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
+ collectionListener.onEntryAdded(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
// THEN only promote mEntry
- assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+ assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
}
@Test
fun testPromotesBindingHUN() {
// GIVEN the current entry should heads up
- whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+ setShouldHeadsUp(entry, true)
// WHEN the notification started binding on the previous run
- mCollectionListener.onEntryAdded(mEntry)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
- verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), any())
+ collectionListener.onEntryAdded(entry)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), any())
// THEN only promote mEntry
- assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+ assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
}
@Test
fun testPromotesCurrentHUN() {
// GIVEN the current HUN is set to mEntry
- addHUN(mEntry)
+ addHUN(entry)
// THEN only promote the current HUN, mEntry
- assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
- assertFalse(mNotifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
+ assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
+ assertFalse(notifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
.setPkg("test-package2")
.build()))
}
@@ -313,11 +313,11 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
@Test
fun testIncludeInSectionCurrentHUN() {
// GIVEN the current HUN is set to mEntry
- addHUN(mEntry)
+ addHUN(entry)
// THEN only section the current HUN, mEntry
- assertTrue(mNotifSectioner.isInSection(mEntry))
- assertFalse(mNotifSectioner.isInSection(NotificationEntryBuilder()
+ assertTrue(notifSectioner.isInSection(entry))
+ assertFalse(notifSectioner.isInSection(NotificationEntryBuilder()
.setPkg("test-package")
.build()))
}
@@ -325,11 +325,11 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
@Test
fun testLifetimeExtendsCurrentHUN() {
// GIVEN there is a HUN, mEntry
- addHUN(mEntry)
+ addHUN(entry)
// THEN only the current HUN, mEntry, should be lifetimeExtended
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0))
- assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* cancellationReason */ 0))
+ assertFalse(notifLifetimeExtender.maybeExtendLifetime(
NotificationEntryBuilder()
.setPkg("test-package")
.build(), /* cancellationReason */ 0))
@@ -338,726 +338,752 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
@Test
fun testShowHUNOnInflationFinished() {
// WHEN a notification should HUN and its inflation is finished
- whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+ setShouldHeadsUp(entry, true)
- mCollectionListener.onEntryAdded(mEntry)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
- verify(mHeadsUpManager, never()).showNotification(mEntry)
+ collectionListener.onEntryAdded(entry)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
+ verify(headsUpManager, never()).showNotification(entry)
withArgCaptor<BindCallback> {
- verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
- }.onBindFinished(mEntry)
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ }.onBindFinished(entry)
// THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager).showNotification(mEntry)
+ verify(headsUpManager).showNotification(entry)
}
@Test
fun testNoHUNOnInflationFinished() {
// WHEN a notification shouldn't HUN and its inflation is finished
- whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
- mCollectionListener.onEntryAdded(mEntry)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldHeadsUp(entry, false)
+ collectionListener.onEntryAdded(entry)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN we never bind the heads up view or tell HeadsUpManager to show the notification
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
- verify(mHeadsUpManager, never()).showNotification(mEntry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(entry), any())
+ verify(headsUpManager, never()).showNotification(entry)
}
@Test
fun testOnEntryUpdated_toAlert() {
// GIVEN that an entry is posted that should not heads up
- setShouldHeadsUp(mEntry, false)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldHeadsUp(entry, false)
+ collectionListener.onEntryAdded(entry)
// WHEN it's updated to heads up
- setShouldHeadsUp(mEntry)
- mCollectionListener.onEntryUpdated(mEntry)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldHeadsUp(entry)
+ collectionListener.onEntryUpdated(entry)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification alerts
- finishBind(mEntry)
- verify(mHeadsUpManager).showNotification(mEntry)
+ finishBind(entry)
+ verify(headsUpManager).showNotification(entry)
}
@Test
fun testOnEntryUpdated_toNotAlert() {
// GIVEN that an entry is posted that should heads up
- setShouldHeadsUp(mEntry)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldHeadsUp(entry)
+ collectionListener.onEntryAdded(entry)
// WHEN it's updated to not heads up
- setShouldHeadsUp(mEntry, false)
- mCollectionListener.onEntryUpdated(mEntry)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldHeadsUp(entry, false)
+ collectionListener.onEntryUpdated(entry)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
}
@Test
fun testOnEntryRemovedRemovesHeadsUpNotification() {
// GIVEN the current HUN is mEntry
- addHUN(mEntry)
+ addHUN(entry)
// WHEN mEntry is removed from the notification collection
- mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0)
- whenever(mRemoteInputManager.isSpinning(any())).thenReturn(false)
+ collectionListener.onEntryRemoved(entry, /* cancellation reason */ 0)
+ whenever(remoteInputManager.isSpinning(any())).thenReturn(false)
// THEN heads up manager should remove the entry
- verify(mHeadsUpManager).removeNotification(mEntry.key, false)
+ verify(headsUpManager).removeNotification(entry.key, false)
}
private fun addHUN(entry: NotificationEntry) {
- mHuns.add(entry)
- whenever(mHeadsUpManager.topEntry).thenReturn(entry)
- mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
- mNotifLifetimeExtender.cancelLifetimeExtension(entry)
+ huns.add(entry)
+ whenever(headsUpManager.topEntry).thenReturn(entry)
+ onHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
+ notifLifetimeExtender.cancelLifetimeExtension(entry)
}
@Test
fun testTransferIsolatedChildAlert_withGroupAlertSummary() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupSibling1))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupSibling1))
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupSibling1)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupSibling1))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupSibling1))
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupSibling1)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupSibling1))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupSibling1))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupSibling1)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupSibling1)
// In addition make sure we have explicitly marked the summary as having interrupted due
// to the alert being transferred
- assertTrue(mGroupSummary.hasInterrupted())
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferIsolatedChildAlert_withGroupAlertAll() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mGroupSummary, mGroupChild1))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupChild1))
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupChild1)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mGroupChild1))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mGroupChild1))
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupChild1)
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupChild1))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupChild1))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupChild1)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupChild1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupChild1)
- assertTrue(mGroupSummary.hasInterrupted())
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupChild1)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferTwoIsolatedChildAlert_withGroupAlertSummary() {
// WHEN a notification should HUN and its inflation is finished
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
-
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupSibling1)
- mCollectionListener.onEntryAdded(mGroupSibling2)
- val entryList = listOf(mGroupSibling1, mGroupSibling2)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
-
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupSibling1)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2, groupPriority))
+
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupSibling1)
+ collectionListener.onEntryAdded(groupSibling2)
+ val entryList = listOf(groupSibling1, groupSibling2)
+ beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupSibling1)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
// THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
- assertTrue(mGroupSummary.hasInterrupted())
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferTwoIsolatedChildAlert_withGroupAlertAll() {
// WHEN a notification should HUN and its inflation is finished
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2, mGroupPriority))
-
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupChild1)
- mCollectionListener.onEntryAdded(mGroupChild2)
- val entryList = listOf(mGroupChild1, mGroupChild2)
- mBeforeTransformGroupsListener.onBeforeTransformGroups(entryList)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
-
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupChild1)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupChild1, groupChild2, groupPriority))
+
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupChild1)
+ collectionListener.onEntryAdded(groupChild2)
+ val entryList = listOf(groupChild1, groupChild2)
+ beforeTransformGroupsListener.onBeforeTransformGroups(entryList)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(entryList)
+
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupChild1)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
// THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupChild1)
- verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
- assertTrue(mGroupSummary.hasInterrupted())
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupChild1)
+ verify(headsUpManager, never()).showNotification(groupChild2)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferToPriorityOnAddWithTwoSiblings() {
// WHEN a notification should HUN and its inflation is finished
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2, groupPriority))
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupPriority)
- mCollectionListener.onEntryAdded(mGroupSibling1)
- mCollectionListener.onEntryAdded(mGroupSibling2)
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupPriority)
+ collectionListener.onEntryAdded(groupSibling1)
+ collectionListener.onEntryAdded(groupSibling2)
val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
val afterTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
.build()
- mBeforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
+ beforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupPriority)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupPriority)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
// THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupPriority)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
- assertTrue(mGroupSummary.hasInterrupted())
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupPriority)
+ verify(headsUpManager, never()).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferToPriorityOnUpdateWithTwoSiblings() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2, groupPriority))
- mCollectionListener.onEntryUpdated(mGroupSummary)
- mCollectionListener.onEntryUpdated(mGroupPriority)
- mCollectionListener.onEntryUpdated(mGroupSibling1)
- mCollectionListener.onEntryUpdated(mGroupSibling2)
+ collectionListener.onEntryUpdated(groupSummary)
+ collectionListener.onEntryUpdated(groupPriority)
+ collectionListener.onEntryUpdated(groupSibling1)
+ collectionListener.onEntryUpdated(groupSibling2)
val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
val afterTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
.build()
- mBeforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
-
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupPriority)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
-
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupPriority)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
- assertTrue(mGroupSummary.hasInterrupted())
+ beforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupPriority)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupPriority)
+ verify(headsUpManager, never()).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testTransferToPriorityOnUpdateWithTwoNonUpdatedSiblings() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2, groupPriority))
- mCollectionListener.onEntryUpdated(mGroupSummary)
- mCollectionListener.onEntryUpdated(mGroupPriority)
+ collectionListener.onEntryUpdated(groupSummary)
+ collectionListener.onEntryUpdated(groupPriority)
val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
val afterTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
.build()
- mBeforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
-
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupPriority)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
-
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupPriority)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
- assertTrue(mGroupSummary.hasInterrupted())
+ beforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupPriority)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupPriority)
+ verify(headsUpManager, never()).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
+ assertTrue(groupSummary.hasInterrupted())
}
@Test
fun testNoTransferToPriorityOnUpdateOfTwoSiblings() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2, mGroupPriority))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2, groupPriority))
- mCollectionListener.onEntryUpdated(mGroupSummary)
- mCollectionListener.onEntryUpdated(mGroupSibling1)
- mCollectionListener.onEntryUpdated(mGroupSibling2)
+ collectionListener.onEntryUpdated(groupSummary)
+ collectionListener.onEntryUpdated(groupSibling1)
+ collectionListener.onEntryUpdated(groupSibling2)
val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupPriority, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
val afterTransformGroup = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
.build()
- mBeforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(mGroupPriority, afterTransformGroup))
-
- finishBind(mGroupSummary)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupPriority), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
-
- verify(mHeadsUpManager).showNotification(mGroupSummary)
- verify(mHeadsUpManager, never()).showNotification(mGroupPriority)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ beforeFinalizeFilterListener
+ .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+
+ finishBind(groupSummary)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
+
+ verify(headsUpManager).showNotification(groupSummary)
+ verify(headsUpManager, never()).showNotification(groupPriority)
+ verify(headsUpManager, never()).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
}
@Test
fun testNoTransferTwoChildAlert_withGroupAlertSummary() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupSibling1, mGroupSibling2))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupSibling1, groupSibling2))
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupSibling1)
- mCollectionListener.onEntryAdded(mGroupSibling2)
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupSibling1)
+ collectionListener.onEntryAdded(groupSibling2)
val groupEntry = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupSibling1, mGroupSibling2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
- finishBind(mGroupSummary)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSibling2), any())
+ finishBind(groupSummary)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSibling2), any())
- verify(mHeadsUpManager).showNotification(mGroupSummary)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling1)
- verify(mHeadsUpManager, never()).showNotification(mGroupSibling2)
+ verify(headsUpManager).showNotification(groupSummary)
+ verify(headsUpManager, never()).showNotification(groupSibling1)
+ verify(headsUpManager, never()).showNotification(groupSibling2)
}
@Test
fun testNoTransferTwoChildAlert_withGroupAlertAll() {
- setShouldHeadsUp(mGroupSummary)
- whenever(mNotifPipeline.allNotifs)
- .thenReturn(listOf(mGroupSummary, mGroupChild1, mGroupChild2))
+ setShouldHeadsUp(groupSummary)
+ whenever(notifPipeline.allNotifs)
+ .thenReturn(listOf(groupSummary, groupChild1, groupChild2))
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupChild1)
- mCollectionListener.onEntryAdded(mGroupChild2)
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupChild1)
+ collectionListener.onEntryAdded(groupChild2)
val groupEntry = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupChild1, mGroupChild2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupChild1, groupChild2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
- finishBind(mGroupSummary)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild1), any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
+ finishBind(groupSummary)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild1), any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
- verify(mHeadsUpManager).showNotification(mGroupSummary)
- verify(mHeadsUpManager, never()).showNotification(mGroupChild1)
- verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
+ verify(headsUpManager).showNotification(groupSummary)
+ verify(headsUpManager, never()).showNotification(groupChild1)
+ verify(headsUpManager, never()).showNotification(groupChild2)
}
@Test
fun testNoTransfer_groupSummaryNotAlerting() {
// When we have a group where the summary should not alert and exactly one child should
// alert, we should never mark the group summary as interrupted (because it doesn't).
- setShouldHeadsUp(mGroupSummary, false)
- setShouldHeadsUp(mGroupChild1, true)
- setShouldHeadsUp(mGroupChild2, false)
+ setShouldHeadsUp(groupSummary, false)
+ setShouldHeadsUp(groupChild1, true)
+ setShouldHeadsUp(groupChild2, false)
- mCollectionListener.onEntryAdded(mGroupSummary)
- mCollectionListener.onEntryAdded(mGroupChild1)
- mCollectionListener.onEntryAdded(mGroupChild2)
+ collectionListener.onEntryAdded(groupSummary)
+ collectionListener.onEntryAdded(groupChild1)
+ collectionListener.onEntryAdded(groupChild2)
val groupEntry = GroupEntryBuilder()
- .setSummary(mGroupSummary)
- .setChildren(listOf(mGroupChild1, mGroupChild2))
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupChild1, groupChild2))
.build()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
-
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupSummary), any())
- finishBind(mGroupChild1)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mGroupChild2), any())
-
- verify(mHeadsUpManager, never()).showNotification(mGroupSummary)
- verify(mHeadsUpManager).showNotification(mGroupChild1)
- verify(mHeadsUpManager, never()).showNotification(mGroupChild2)
- assertFalse(mGroupSummary.hasInterrupted())
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
+ finishBind(groupChild1)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupChild2), any())
+
+ verify(headsUpManager, never()).showNotification(groupSummary)
+ verify(headsUpManager).showNotification(groupChild1)
+ verify(headsUpManager, never()).showNotification(groupChild2)
+ assertFalse(groupSummary.hasInterrupted())
}
@Test
fun testOnRankingApplied_newEntryShouldAlert() {
// GIVEN that mEntry has never interrupted in the past, and now should
// and is new enough to do so
- assertFalse(mEntry.hasInterrupted())
- mCoordinator.setUpdateTime(mEntry, mSystemClock.currentTimeMillis())
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
+ assertFalse(entry.hasInterrupted())
+ coordinator.setUpdateTime(entry, systemClock.currentTimeMillis())
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
// WHEN a ranking applied update occurs
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is shown
- finishBind(mEntry)
- verify(mHeadsUpManager).showNotification(mEntry)
+ finishBind(entry)
+ verify(headsUpManager).showNotification(entry)
}
@Test
fun testOnRankingApplied_alreadyAlertedEntryShouldNotAlertAgain() {
// GIVEN that mEntry has alerted in the past, even if it's new
- mEntry.setInterruption()
- mCoordinator.setUpdateTime(mEntry, mSystemClock.currentTimeMillis())
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
+ entry.setInterruption()
+ coordinator.setUpdateTime(entry, systemClock.currentTimeMillis())
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
// WHEN a ranking applied update occurs
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
}
@Test
fun testOnRankingApplied_entryUpdatedToHun() {
// GIVEN that mEntry is added in a state where it should not HUN
- setShouldHeadsUp(mEntry, false)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldHeadsUp(entry, false)
+ collectionListener.onEntryAdded(entry)
// and it is then updated such that it should now HUN
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
// WHEN a ranking applied update occurs
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is shown
- finishBind(mEntry)
- verify(mHeadsUpManager).showNotification(mEntry)
+ finishBind(entry)
+ verify(headsUpManager).showNotification(entry)
}
@Test
fun testOnRankingApplied_entryUpdatedButTooOld() {
// GIVEN that mEntry is added in a state where it should not HUN
- setShouldHeadsUp(mEntry, false)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldHeadsUp(entry, false)
+ collectionListener.onEntryAdded(entry)
// and it was actually added 10s ago
- mCoordinator.setUpdateTime(mEntry, mSystemClock.currentTimeMillis() - 10000)
+ coordinator.setUpdateTime(entry, systemClock.currentTimeMillis() - 10000)
// WHEN it is updated to HUN and then a ranking update occurs
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN the notification is never bound or shown
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
}
@Test
fun onEntryAdded_whenLaunchingFSI_doesLogDecision() {
// GIVEN A new notification can FSI
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- mCollectionListener.onEntryAdded(mEntry)
-
- verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry)
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(
- mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ collectionListener.onEntryAdded(entry)
+
+ verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+ )
}
@Test
fun onEntryAdded_whenNotLaunchingFSI_doesLogDecision() {
// GIVEN A new notification can't FSI
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
+ collectionListener.onEntryAdded(entry)
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(
- mEntry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verifyLoggedFullScreenIntentDecision(entry, FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
}
@Test
fun onEntryAdded_whenNotLaunchingFSIBecauseOfDnd_doesLogDecision() {
// GIVEN A new notification can't FSI because of DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
-
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(
- mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
+
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+ )
}
@Test
fun testOnRankingApplied_noFSIOnUpdateWhenFlagOff() {
// Ensure the feature flag is off
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(false)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(false)
// GIVEN that mEntry was previously suppressed from full-screen only by DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
// Verify that this causes a log
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(
- mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- clearInvocations(mNotificationInterruptStateProvider)
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+ )
+ clearInterruptionProviderInvocations()
// and it is then updated to allow full screen
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
- mCollectionListener.onRankingApplied()
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
+ collectionListener.onRankingApplied()
// THEN it should not full screen because the feature is off
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
// VERIFY that no additional logging happens either
- verify(mNotificationInterruptStateProvider, never())
- .logFullScreenIntentDecision(any(), any())
+ verifyNoFullScreenIntentDecisionLogged()
}
@Test
fun testOnRankingApplied_updateToFullScreen() {
// Turn on the feature
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(true)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
// GIVEN that mEntry was previously suppressed from full-screen only by DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
// at this point, it should not have full screened, but should have logged
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(mEntry,
- FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- clearInvocations(mNotificationInterruptStateProvider)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+ )
+ clearInterruptionProviderInvocations()
// and it is then updated to allow full screen AND HUN
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN it should full screen and log but it should NOT HUN
- verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(mEntry,
- FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- clearInvocations(mNotificationInterruptStateProvider)
+ verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+ )
+ clearInterruptionProviderInvocations()
// WHEN ranking updates again and the pipeline reruns
- clearInvocations(mLaunchFullScreenIntentProvider)
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ clearInvocations(launchFullScreenIntentProvider)
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// VERIFY that the FSI does not launch again or log
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mNotificationInterruptStateProvider, never())
- .logFullScreenIntentDecision(any(), any())
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verifyNoFullScreenIntentDecisionLogged()
}
@Test
fun testOnRankingApplied_withOnlyDndSuppressionAllowsFsiLater() {
// Turn on the feature
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(true)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
// GIVEN that mEntry was previously suppressed from full-screen only by DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
// at this point, it should not have full screened, but should have logged
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(mEntry,
- FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- clearInvocations(mNotificationInterruptStateProvider)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
+ )
+ clearInterruptionProviderInvocations()
// ranking is applied with only DND blocking FSI
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN it should still not yet full screen or HUN
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
// Same decision as before; is not logged
- verify(mNotificationInterruptStateProvider, never())
- .logFullScreenIntentDecision(any(), any())
- clearInvocations(mNotificationInterruptStateProvider)
+ verifyNoFullScreenIntentDecisionLogged()
+ clearInterruptionProviderInvocations()
// and it is then updated to allow full screen AND HUN
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- setShouldHeadsUp(mEntry)
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ setShouldHeadsUp(entry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN it should full screen and log but it should NOT HUN
- verify(mLaunchFullScreenIntentProvider).launchFullScreenIntent(mEntry)
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
- verify(mNotificationInterruptStateProvider).logFullScreenIntentDecision(mEntry,
- FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- clearInvocations(mNotificationInterruptStateProvider)
+ verify(launchFullScreenIntentProvider).launchFullScreenIntent(entry)
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
+ verifyLoggedFullScreenIntentDecision(
+ entry,
+ FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE
+ )
+ clearInterruptionProviderInvocations()
}
@Test
fun testOnRankingApplied_newNonFullScreenAnswerInvalidatesCandidate() {
// Turn on the feature
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(true)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
// GIVEN that mEntry was previously suppressed from full-screen only by DND
- whenever(mNotifPipeline.allNotifs).thenReturn(listOf(mEntry))
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(entry))
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
// at this point, it should not have full screened
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(mEntry)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(entry)
// now some other condition blocks FSI in addition to DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND)
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND)
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// THEN it should NOT full screen or HUN
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any())
- verify(mHeadsUpManager, never()).showNotification(any())
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
+ verify(headsUpManager, never()).showNotification(any())
// NOW the DND logic changes and FSI and HUN are available
- clearInvocations(mLaunchFullScreenIntentProvider)
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- setShouldHeadsUp(mEntry)
- mCollectionListener.onRankingApplied()
- mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry))
- mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry))
+ clearInvocations(launchFullScreenIntentProvider)
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ setShouldHeadsUp(entry)
+ collectionListener.onRankingApplied()
+ beforeTransformGroupsListener.onBeforeTransformGroups(listOf(entry))
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
// VERIFY that the FSI didn't happen, but that we do HUN
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
- finishBind(mEntry)
- verify(mHeadsUpManager).showNotification(mEntry)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(any())
+ finishBind(entry)
+ verify(headsUpManager).showNotification(entry)
}
@Test
fun testOnRankingApplied_noFSIWhenAlsoSuppressedForOtherReasons() {
// Feature on
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(true)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
// GIVEN that mEntry is suppressed by DND (functionally), but not *only* DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND)
+ collectionListener.onEntryAdded(entry)
// and it is updated to full screen later
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
- mCollectionListener.onRankingApplied()
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE)
+ collectionListener.onRankingApplied()
// THEN it should still not full screen because something else was blocking it before
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(mEntry)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(entry)
}
@Test
fun testOnRankingApplied_noFSIWhenTooOld() {
// Feature on
- whenever(mFlags.fsiOnDNDUpdate()).thenReturn(true)
+ whenever(flags.fsiOnDNDUpdate()).thenReturn(true)
// GIVEN that mEntry is suppressed only by DND
- setShouldFullScreen(mEntry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
- mCollectionListener.onEntryAdded(mEntry)
+ setShouldFullScreen(entry, FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND)
+ collectionListener.onEntryAdded(entry)
// but it's >10s old
- mCoordinator.addForFSIReconsideration(mEntry, mSystemClock.currentTimeMillis() - 10000)
+ coordinator.addForFSIReconsideration(entry, systemClock.currentTimeMillis() - 10000)
// and it is updated to full screen later
- setShouldFullScreen(mEntry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
- mCollectionListener.onRankingApplied()
+ setShouldFullScreen(entry, FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN)
+ collectionListener.onRankingApplied()
// THEN it should still not full screen because it's too old
- verify(mLaunchFullScreenIntentProvider, never()).launchFullScreenIntent(mEntry)
+ verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(entry)
}
private fun setShouldHeadsUp(entry: NotificationEntry, should: Boolean = true) {
- whenever(mNotificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should)
- whenever(mNotificationInterruptStateProvider.checkHeadsUp(eq(entry), any()))
+ whenever(notificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should)
+ whenever(notificationInterruptStateProvider.checkHeadsUp(eq(entry), any()))
.thenReturn(should)
}
private fun setShouldFullScreen(entry: NotificationEntry, decision: FullScreenIntentDecision) {
- whenever(mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry))
+ whenever(notificationInterruptStateProvider.getFullScreenIntentDecision(entry))
.thenReturn(decision)
}
+ private fun verifyLoggedFullScreenIntentDecision(
+ entry: NotificationEntry,
+ decision: FullScreenIntentDecision
+ ) {
+ verify(notificationInterruptStateProvider).logFullScreenIntentDecision(entry, decision)
+ }
+
+ private fun verifyNoFullScreenIntentDecisionLogged() {
+ verify(notificationInterruptStateProvider, never())
+ .logFullScreenIntentDecision(any(), any())
+ }
+
+ private fun clearInterruptionProviderInvocations() {
+ clearInvocations(notificationInterruptStateProvider)
+ }
+
private fun finishBind(entry: NotificationEntry) {
- verify(mHeadsUpManager, never()).showNotification(entry)
+ verify(headsUpManager, never()).showNotification(entry)
withArgCaptor<BindCallback> {
- verify(mHeadsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
}.onBindFinished(entry)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 68d67ca20007..89f8bdbfe05b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -89,7 +89,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Mock
private KeyguardViewMediator mKeyguardViewMediator;
@Mock
- private BiometricUnlockController.BiometricModeListener mBiometricModeListener;
+ private BiometricUnlockController.BiometricUnlockEventsListener mBiometricUnlockEventsListener;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
@@ -145,7 +145,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mSystemClock
);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
- mBiometricUnlockController.addBiometricModeListener(mBiometricModeListener);
+ mBiometricUnlockController.addListener(mBiometricUnlockEventsListener);
when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 44fbd5b99894..6306a36d9730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
+import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
@@ -29,6 +30,7 @@ class FakeMobileConnectionRepository(
override val subId: Int,
override val tableLogBuffer: TableLogBuffer,
) : MobileConnectionRepository {
+ override val carrierId = MutableStateFlow(UNKNOWN_CARRIER_ID)
override val isEmergencyOnly = MutableStateFlow(false)
override val isRoaming = MutableStateFlow(false)
override val operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index f2bb66a501ec..423c47661fa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -593,7 +593,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
val realRepo =
MobileConnectionRepositoryImpl(
- context,
SUB_ID,
defaultNetworkName = NetworkNameModel.Default("default"),
networkNameSeparator = SEP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 934e1c64c6da..d1df6e3c2072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -42,6 +42,7 @@ import android.telephony.TelephonyManager.DATA_SUSPENDED
import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.ERI_ON
+import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
@@ -116,7 +117,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
underTest =
MobileConnectionRepositoryImpl(
- context,
SUB_1_ID,
DEFAULT_NAME,
SEP,
@@ -359,6 +359,36 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
@Test
+ fun carrierId_initialValueCaptured() =
+ testScope.runTest {
+ whenever(telephonyManager.simCarrierId).thenReturn(1234)
+
+ var latest: Int? = null
+ val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(1234)
+
+ job.cancel()
+ }
+
+ @Test
+ fun carrierId_updatesOnBroadcast() =
+ testScope.runTest {
+ whenever(telephonyManager.simCarrierId).thenReturn(1234)
+
+ var latest: Int? = null
+ val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
+
+ fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
+ receiver.onReceive(context, carrierIdIntent(carrierId = 4321))
+ }
+
+ assertThat(latest).isEqualTo(4321)
+
+ job.cancel()
+ }
+
+ @Test
fun carrierNetworkChange() =
testScope.runTest {
var latest: Boolean? = null
@@ -796,6 +826,15 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
}
+ private fun carrierIdIntent(
+ subId: Int = SUB_1_ID,
+ carrierId: Int,
+ ): Intent =
+ Intent(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED).apply {
+ putExtra(EXTRA_SUBSCRIPTION_ID, subId)
+ putExtra(EXTRA_CARRIER_ID, carrierId)
+ }
+
private fun spnIntent(
subId: Int = SUB_1_ID,
showSpn: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 9da9ff72d380..4f15aed00230 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -117,7 +117,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
underTest =
MobileConnectionRepositoryImpl(
- context,
SUB_1_ID,
DEFAULT_NAME,
SEP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ddff17aef2de..9d294cf098e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -155,7 +155,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
connectionFactory =
MobileConnectionRepositoryImpl.Factory(
fakeBroadcastDispatcher,
- context = context,
telephonyManager = telephonyManager,
bgDispatcher = IMMEDIATE,
logger = logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 8d2c5695c7c4..f054422e6524 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CellSignalStrength
-import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -42,8 +42,10 @@ class FakeMobileIconInteractor(
override val mobileIsDefault = MutableStateFlow(true)
- private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
- override val networkTypeIconGroup = _iconGroup
+ override val networkTypeIconGroup =
+ MutableStateFlow<NetworkTypeIconModel>(
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+ )
override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo mode"))
@@ -73,10 +75,6 @@ class FakeMobileIconInteractor(
override val isForceHidden = MutableStateFlow(false)
- fun setIconGroup(group: SignalIcon.MobileIconGroup) {
- _iconGroup.value = group
- }
-
fun setIsEmergencyOnly(emergency: Boolean) {
_isEmergencyOnly.value = emergency
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index d6fdad417b31..3ced7b2c4e6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -59,7 +59,6 @@ class FakeMobileIconsInteractor(
override val alwaysShowDataRatIcon = MutableStateFlow(false)
override val alwaysUseCdmaLevel = MutableStateFlow(false)
- override val defaultDataSubId = MutableStateFlow(DEFAULT_DATA_SUB_ID)
override val mobileIsDefault = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 2054e8b12eff..8d7f0f6035cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -19,7 +19,8 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CellSignalStrength
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
-import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
@@ -31,18 +32,24 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobile
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.yield
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class MobileIconInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconInteractor
@@ -50,29 +57,17 @@ class MobileIconInteractorTest : SysuiTestCase() {
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock())
- private val scope = CoroutineScope(IMMEDIATE)
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
- underTest =
- MobileIconInteractorImpl(
- scope,
- mobileIconsInteractor.activeDataConnectionHasDataEnabled,
- mobileIconsInteractor.alwaysShowDataRatIcon,
- mobileIconsInteractor.alwaysUseCdmaLevel,
- mobileIconsInteractor.mobileIsDefault,
- mobileIconsInteractor.defaultMobileIconMapping,
- mobileIconsInteractor.defaultMobileIconGroup,
- mobileIconsInteractor.defaultDataSubId,
- mobileIconsInteractor.isDefaultConnectionFailed,
- mobileIconsInteractor.isForceHidden,
- connectionRepository,
- )
+ underTest = createInteractor()
}
@Test
fun gsm_level_default_unknown() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = true
var latest: Int? = null
@@ -85,7 +80,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun gsm_usesGsmLevel() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = true
connectionRepository.primaryLevel.value = GSM_LEVEL
connectionRepository.cdmaLevel.value = CDMA_LEVEL
@@ -100,7 +95,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = true
connectionRepository.primaryLevel.value = GSM_LEVEL
connectionRepository.cdmaLevel.value = CDMA_LEVEL
@@ -116,7 +111,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun notGsm_level_default_unknown() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = false
var latest: Int? = null
@@ -128,7 +123,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = false
connectionRepository.primaryLevel.value = GSM_LEVEL
connectionRepository.cdmaLevel.value = CDMA_LEVEL
@@ -144,7 +139,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.isGsm.value = false
connectionRepository.primaryLevel.value = GSM_LEVEL
connectionRepository.cdmaLevel.value = CDMA_LEVEL
@@ -160,7 +155,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun numberOfLevels_comesFromRepo() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Int? = null
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
@@ -175,101 +170,106 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun iconGroup_three_g() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.resolvedNetworkType.value =
DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(TelephonyIcons.THREE_G)
+ assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G))
job.cancel()
}
@Test
fun iconGroup_updates_on_change() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.resolvedNetworkType.value =
DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
connectionRepository.resolvedNetworkType.value =
DefaultNetworkType(mobileMappingsProxy.toIconKey(FOUR_G))
- yield()
- assertThat(latest).isEqualTo(TelephonyIcons.FOUR_G)
+ assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.FOUR_G))
job.cancel()
}
@Test
fun iconGroup_5g_override_type() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.resolvedNetworkType.value =
OverrideNetworkType(mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE))
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(TelephonyIcons.NR_5G)
+ assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.NR_5G))
job.cancel()
}
@Test
fun iconGroup_default_if_no_lookup() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.resolvedNetworkType.value =
DefaultNetworkType(mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN))
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(FakeMobileIconsInteractor.DEFAULT_ICON)
+ assertThat(latest)
+ .isEqualTo(NetworkTypeIconModel.DefaultIcon(FakeMobileIconsInteractor.DEFAULT_ICON))
job.cancel()
}
@Test
fun iconGroup_carrierMerged_usesOverride() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
connectionRepository.resolvedNetworkType.value = CarrierMergedNetworkType
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(CarrierMergedNetworkType.iconGroupOverride)
+ assertThat(latest)
+ .isEqualTo(
+ NetworkTypeIconModel.DefaultIcon(CarrierMergedNetworkType.iconGroupOverride)
+ )
job.cancel()
}
@Test
- fun `icon group - checks default data`() =
- runBlocking(IMMEDIATE) {
- mobileIconsInteractor.defaultDataSubId.value = SUB_1_ID
+ fun overrideIcon_usesCarrierIdOverride() =
+ testScope.runTest {
+ val overrides =
+ mock<MobileIconCarrierIdOverrides>().also {
+ whenever(it.carrierIdEntryExists(anyInt())).thenReturn(true)
+ whenever(it.getOverrideFor(anyInt(), anyString(), any())).thenReturn(1234)
+ }
+
+ underTest = createInteractor(overrides)
+
connectionRepository.resolvedNetworkType.value =
DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
- var latest: MobileIconGroup? = null
+ var latest: NetworkTypeIconModel? = null
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(TelephonyIcons.THREE_G)
-
- // Default data sub id changes to something else
- mobileIconsInteractor.defaultDataSubId.value = 123
- yield()
-
- assertThat(latest).isEqualTo(TelephonyIcons.NOT_DEFAULT_DATA)
+ assertThat(latest)
+ .isEqualTo(NetworkTypeIconModel.OverriddenIcon(TelephonyIcons.THREE_G, 1234))
job.cancel()
}
@Test
fun alwaysShowDataRatIcon_matchesParent() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
@@ -284,7 +284,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun alwaysUseCdmaLevel_matchesParent() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
@@ -299,7 +299,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun test_isDefaultDataEnabled_matchesParent() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDefaultDataEnabled.onEach { latest = it }.launchIn(this)
@@ -314,7 +314,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun test_isDefaultConnectionFailed_matchedParent() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
val job = underTest.isDefaultConnectionFailed.launchIn(this)
mobileIconsInteractor.isDefaultConnectionFailed.value = false
@@ -328,12 +328,11 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun dataState_connected() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
connectionRepository.dataConnectionState.value = DataConnectionState.Connected
- yield()
assertThat(latest).isTrue()
@@ -342,7 +341,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun dataState_notConnected() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
@@ -355,7 +354,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun `isInService - uses repository value`() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isInService.onEach { latest = it }.launchIn(this)
@@ -372,19 +371,17 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun `roaming - is gsm - uses connection model`() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
connectionRepository.cdmaRoaming.value = true
connectionRepository.isGsm.value = true
connectionRepository.isRoaming.value = false
- yield()
assertThat(latest).isFalse()
connectionRepository.isRoaming.value = true
- yield()
assertThat(latest).isTrue()
@@ -393,21 +390,19 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun `roaming - is cdma - uses cdma roaming bit`() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
connectionRepository.cdmaRoaming.value = false
connectionRepository.isGsm.value = false
connectionRepository.isRoaming.value = true
- yield()
assertThat(latest).isFalse()
connectionRepository.cdmaRoaming.value = true
connectionRepository.isGsm.value = false
connectionRepository.isRoaming.value = false
- yield()
assertThat(latest).isTrue()
@@ -416,7 +411,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun `roaming - false while carrierNetworkChangeActive`() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -424,13 +419,11 @@ class MobileIconInteractorTest : SysuiTestCase() {
connectionRepository.isGsm.value = false
connectionRepository.isRoaming.value = true
connectionRepository.carrierNetworkChangeActive.value = true
- yield()
assertThat(latest).isFalse()
connectionRepository.cdmaRoaming.value = true
connectionRepository.isGsm.value = true
- yield()
assertThat(latest).isFalse()
@@ -439,7 +432,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun `network name - uses operatorAlphaShot when non null and repo is default`() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -448,20 +441,17 @@ class MobileIconInteractorTest : SysuiTestCase() {
// Default network name, operator name is non-null, uses the operator name
connectionRepository.networkName.value = DEFAULT_NAME
connectionRepository.operatorAlphaShort.value = testOperatorName
- yield()
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName))
// Default network name, operator name is null, uses the default
connectionRepository.operatorAlphaShort.value = null
- yield()
assertThat(latest).isEqualTo(DEFAULT_NAME)
// Derived network name, operator name non-null, uses the derived name
connectionRepository.networkName.value = DERIVED_NAME
connectionRepository.operatorAlphaShort.value = testOperatorName
- yield()
assertThat(latest).isEqualTo(DERIVED_NAME)
@@ -470,7 +460,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
@Test
fun isForceHidden_matchesParent() =
- runBlocking(IMMEDIATE) {
+ testScope.runTest {
var latest: Boolean? = null
val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
@@ -483,9 +473,25 @@ class MobileIconInteractorTest : SysuiTestCase() {
job.cancel()
}
- companion object {
- private val IMMEDIATE = Dispatchers.Main.immediate
+ private fun createInteractor(
+ overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
+ ) =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled,
+ mobileIconsInteractor.alwaysShowDataRatIcon,
+ mobileIconsInteractor.alwaysUseCdmaLevel,
+ mobileIconsInteractor.mobileIsDefault,
+ mobileIconsInteractor.defaultMobileIconMapping,
+ mobileIconsInteractor.defaultMobileIconGroup,
+ mobileIconsInteractor.isDefaultConnectionFailed,
+ mobileIconsInteractor.isForceHidden,
+ connectionRepository,
+ context,
+ overrides,
+ )
+ companion object {
private const val GSM_LEVEL = 1
private const val CDMA_LEVEL = 2
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 898e89770394..c5ceacac6446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -88,6 +88,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
connectivityRepository,
userSetupRepository,
testScope.backgroundScope,
+ context,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index a6d915243f60..e99be864e73f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -24,6 +24,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
@@ -71,9 +72,9 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
setLevel(1)
setIsDefaultDataEnabled(true)
setIsFailedConnection(false)
- setIconGroup(TelephonyIcons.THREE_G)
setIsEmergencyOnly(false)
setNumberOfLevels(4)
+ networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
isDataConnected.value = true
}
commonImpl =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 1593e5c735a5..1b6ab4d7af96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -77,9 +78,9 @@ class MobileIconViewModelTest : SysuiTestCase() {
setLevel(1)
setIsDefaultDataEnabled(true)
setIsFailedConnection(false)
- setIconGroup(THREE_G)
setIsEmergencyOnly(false)
setNumberOfLevels(4)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
isDataConnected.value = true
}
createAndSetViewModel()
@@ -256,7 +257,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -267,10 +268,11 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
- fun networkType_nullWhenDisabled() =
+ fun networkType_null_whenDisabled() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.setIsDataEnabled(false)
+ interactor.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -280,15 +282,21 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
@Test
- fun networkType_nullWhenFailedConnection() =
+ fun networkTypeIcon_notNull_whenEnabled() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription)
+ )
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.setIsDataEnabled(true)
- interactor.setIsFailedConnection(true)
+ interactor.isDataConnected.value = true
+ interactor.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ assertThat(latest).isEqualTo(expected)
job.cancel()
}
@@ -302,11 +310,11 @@ class MobileIconViewModelTest : SysuiTestCase() {
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
assertThat(latest).isEqualTo(initial)
interactor.isDataConnected.value = false
@@ -325,7 +333,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.setIsDataEnabled(true)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -343,7 +351,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun networkType_alwaysShow_shownEvenWhenDisabled() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.setIsDataEnabled(false)
interactor.alwaysShowDataRatIcon.value = true
@@ -363,7 +371,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun networkType_alwaysShow_shownEvenWhenDisconnected() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.isDataConnected.value = false
interactor.alwaysShowDataRatIcon.value = true
@@ -383,7 +391,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.setIsFailedConnection(true)
interactor.alwaysShowDataRatIcon.value = true
@@ -404,7 +412,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
fun networkType_alwaysShow_notShownWhenInvalidDataTypeIcon() =
testScope.runTest {
// The UNKNOWN icon group doesn't have a valid data type icon ID
- interactor.setIconGroup(UNKNOWN)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(UNKNOWN)
interactor.alwaysShowDataRatIcon.value = true
var latest: Icon? = null
@@ -418,7 +426,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun `network type - alwaysShow - shown when not default`() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.mobileIsDefault.value = false
interactor.alwaysShowDataRatIcon.value = true
@@ -438,7 +446,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
@Test
fun `network type - not shown when not default`() =
testScope.runTest {
- interactor.setIconGroup(THREE_G)
+ interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.isDataConnected.value = true
interactor.mobileIsDefault.value = false
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index d1f9109094ae..ae88f24ab409 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -348,6 +348,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
ResultReceiver resultReceiver) {
+ Objects.requireNonNull(pendingIntent);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(displayId)) {
throw new SecurityException("Display ID " + displayId
@@ -498,6 +499,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
super.createVirtualDpad_enforcePermission();
+ Objects.requireNonNull(config);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
@@ -518,6 +520,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) {
super.createVirtualKeyboard_enforcePermission();
+ Objects.requireNonNull(config);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
@@ -540,6 +543,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) {
super.createVirtualMouse_enforcePermission();
+ Objects.requireNonNull(config);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
@@ -561,6 +565,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void createVirtualTouchscreen(VirtualTouchscreenConfig config,
@NonNull IBinder deviceToken) {
super.createVirtualTouchscreen_enforcePermission();
+ Objects.requireNonNull(config);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
@@ -591,6 +596,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void createVirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config,
@NonNull IBinder deviceToken) {
super.createVirtualNavigationTouchpad_enforcePermission();
+ Objects.requireNonNull(config);
synchronized (mVirtualDeviceLock) {
if (!mVirtualDisplays.contains(config.getAssociatedDisplayId())) {
throw new SecurityException(
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index eec898f21800..291c05877c17 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -68,6 +68,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -309,6 +310,9 @@ public class VirtualDeviceManagerService extends SystemService {
if (associationInfo == null) {
throw new IllegalArgumentException("No association with ID " + associationId);
}
+ Objects.requireNonNull(params);
+ Objects.requireNonNull(activityListener);
+ Objects.requireNonNull(soundEffectListener);
synchronized (mVirtualDeviceManagerLock) {
if (mVirtualDevices.size() == 0) {
@@ -340,6 +344,7 @@ public class VirtualDeviceManagerService extends SystemService {
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IVirtualDevice virtualDevice, String packageName)
throws RemoteException {
+ Objects.requireNonNull(virtualDisplayConfig);
final int callingUid = getCallingUid();
if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
throw new SecurityException(
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3f68ccc64787..ae5dbe11495a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -151,6 +151,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_USE_TIERED_CACHED_ADJ = "use_tiered_cached_adj";
static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time";
+ static final String KEY_USE_MODERN_TRIM = "use_modern_trim";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
@@ -212,6 +213,8 @@ final class ActivityManagerConstants extends ContentObserver {
private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
+ private static final boolean DEFAULT_USE_MODERN_TRIM = false;
+
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -1052,6 +1055,9 @@ final class ActivityManagerConstants extends ContentObserver {
/** @see #KEY_TIERED_CACHED_ADJ_DECAY_TIME */
public long TIERED_CACHED_ADJ_DECAY_TIME = DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME;
+ /** @see #KEY_USE_MODERN_TRIM */
+ public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
+
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1227,6 +1233,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_TIERED_CACHED_ADJ_DECAY_TIME:
updateUseTieredCachedAdj();
break;
+ case KEY_USE_MODERN_TRIM:
+ updateUseModernTrim();
+ break;
default:
updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
@@ -1997,6 +2006,13 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
}
+ private void updateUseModernTrim() {
+ USE_MODERN_TRIM = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_USE_MODERN_TRIM,
+ DEFAULT_USE_MODERN_TRIM);
+ }
+
private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
ForegroundServiceTypePolicy.getDefaultPolicy()
.updatePermissionEnforcementFlagIfNecessary(name);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 08c1de61d7fb..c343ec24412a 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -534,6 +534,8 @@ class AppErrors {
}
}
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(initialPid,
+ CachedAppOptimizer.UNFREEZE_REASON_PROCESS_END);
proc.scheduleCrashLocked(message, exceptionTypeId, extras);
if (force) {
// If the app is responsive, the scheduled crash will happen as expected
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index ccd739006713..25ac956d328d 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1005,6 +1005,37 @@ public class AppProfiler {
mBgHandler.obtainMessage(BgHandler.MEMORY_PRESSURE_CHANGED, mLastMemoryLevel, memFactor)
.sendToTarget();
}
+
+ if (mService.mConstants.USE_MODERN_TRIM) {
+ // Modern trim is not sent based on lowmem state
+ // Dispatch UI_HIDDEN to processes that need it
+ mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
+ final ProcessProfileRecord profile = app.mProfile;
+ final IApplicationThread thread;
+ final ProcessStateRecord state = app.mState;
+ if (state.hasProcStateChanged()) {
+ state.setProcStateChanged(false);
+ }
+ int procState = app.mState.getCurProcState();
+ if (((procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ && procState < ActivityManager.PROCESS_STATE_CACHED_ACTIVITY)
+ || app.mState.isSystemNoUi()) && app.mProfile.hasPendingUiClean()) {
+ // If this application is now in the background and it
+ // had done UI, then give it the special trim level to
+ // have it free UI resources.
+ if ((thread = app.getThread()) != null) {
+ try {
+ thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ app.mProfile.setPendingUiClean(false);
+ } catch (RemoteException e) {
+
+ }
+ }
+ }
+ });
+ return false;
+ }
+
mLastMemoryLevel = memFactor;
mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
boolean allChanged;
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index d5b8bb44e853..dbb351b23c85 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1050,13 +1050,13 @@ class BroadcastProcessQueue {
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged.
*/
- public void checkHealthLocked() {
- checkHealthLocked(mPending);
- checkHealthLocked(mPendingUrgent);
- checkHealthLocked(mPendingOffload);
+ public void assertHealthLocked() {
+ assertHealthLocked(mPending);
+ assertHealthLocked(mPendingUrgent);
+ assertHealthLocked(mPendingOffload);
}
- private void checkHealthLocked(@NonNull ArrayDeque<SomeArgs> queue) {
+ private void assertHealthLocked(@NonNull ArrayDeque<SomeArgs> queue) {
if (queue.isEmpty()) return;
final Iterator<SomeArgs> it = queue.descendingIterator();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b18997a8d21f..a4bdf61e628f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -246,21 +246,15 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private final Handler.Callback mLocalCallback = (msg) -> {
switch (msg.what) {
case MSG_UPDATE_RUNNING_LIST: {
- synchronized (mService) {
- updateRunningListLocked();
- }
+ updateRunningList();
return true;
}
case MSG_DELIVERY_TIMEOUT_SOFT: {
- synchronized (mService) {
- deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1);
- }
+ deliveryTimeoutSoft((BroadcastProcessQueue) msg.obj, msg.arg1);
return true;
}
case MSG_DELIVERY_TIMEOUT_HARD: {
- synchronized (mService) {
- deliveryTimeoutHardLocked((BroadcastProcessQueue) msg.obj);
- }
+ deliveryTimeoutHard((BroadcastProcessQueue) msg.obj);
return true;
}
case MSG_BG_ACTIVITY_START_TIMEOUT: {
@@ -274,9 +268,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
return true;
}
case MSG_CHECK_HEALTH: {
- synchronized (mService) {
- checkHealthLocked();
- }
+ checkHealth();
return true;
}
}
@@ -365,6 +357,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
+ private void updateRunningList() {
+ synchronized (mService) {
+ updateRunningListLocked();
+ }
+ }
+
/**
* Consider updating the list of "running" queues.
* <p>
@@ -965,6 +963,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
r.resultTo = null;
}
+ private void deliveryTimeoutSoft(@NonNull BroadcastProcessQueue queue,
+ int softTimeoutMillis) {
+ synchronized (mService) {
+ deliveryTimeoutSoftLocked(queue, softTimeoutMillis);
+ }
+ }
+
private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
int softTimeoutMillis) {
if (queue.app != null) {
@@ -981,6 +986,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
+ private void deliveryTimeoutHard(@NonNull BroadcastProcessQueue queue) {
+ synchronized (mService) {
+ deliveryTimeoutHardLocked(queue);
+ }
+ }
+
private void deliveryTimeoutHardLocked(@NonNull BroadcastProcessQueue queue) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_TIMEOUT,
"deliveryTimeoutHardLocked");
@@ -1458,58 +1469,69 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// TODO: implement
}
+ private void checkHealth() {
+ synchronized (mService) {
+ checkHealthLocked();
+ }
+ }
+
+ private void checkHealthLocked() {
+ try {
+ assertHealthLocked();
+
+ // If no health issues found above, check again in the future
+ mLocalHandler.sendEmptyMessageDelayed(MSG_CHECK_HEALTH,
+ DateUtils.MINUTE_IN_MILLIS);
+
+ } catch (Exception e) {
+ // Throw up a message to indicate that something went wrong, and
+ // dump current state for later inspection
+ Slog.wtf(TAG, e);
+ dumpToDropBoxLocked(e.toString());
+ }
+ }
+
/**
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged. If we determine we're in an unhealthy state, dump
* current state once and stop future health checks to avoid spamming.
*/
@VisibleForTesting
- void checkHealthLocked() {
- try {
- // Verify all runnable queues are sorted
- BroadcastProcessQueue prev = null;
- BroadcastProcessQueue next = mRunnableHead;
- while (next != null) {
- checkState(next.runnableAtPrev == prev, "runnableAtPrev");
- checkState(next.isRunnable(), "isRunnable " + next);
- if (prev != null) {
- checkState(next.getRunnableAt() >= prev.getRunnableAt(),
- "getRunnableAt " + next + " vs " + prev);
- }
- prev = next;
- next = next.runnableAtNext;
+ void assertHealthLocked() {
+ // Verify all runnable queues are sorted
+ BroadcastProcessQueue prev = null;
+ BroadcastProcessQueue next = mRunnableHead;
+ while (next != null) {
+ checkState(next.runnableAtPrev == prev, "runnableAtPrev");
+ checkState(next.isRunnable(), "isRunnable " + next);
+ if (prev != null) {
+ checkState(next.getRunnableAt() >= prev.getRunnableAt(),
+ "getRunnableAt " + next + " vs " + prev);
}
+ prev = next;
+ next = next.runnableAtNext;
+ }
- // Verify all running queues are active
- for (BroadcastProcessQueue queue : mRunning) {
- if (queue != null) {
- checkState(queue.isActive(), "isActive " + queue);
- }
+ // Verify all running queues are active
+ for (BroadcastProcessQueue queue : mRunning) {
+ if (queue != null) {
+ checkState(queue.isActive(), "isActive " + queue);
}
+ }
- // Verify that pending cold start hasn't been orphaned
- if (mRunningColdStart != null) {
- checkState(getRunningIndexOf(mRunningColdStart) >= 0,
- "isOrphaned " + mRunningColdStart);
- }
+ // Verify that pending cold start hasn't been orphaned
+ if (mRunningColdStart != null) {
+ checkState(getRunningIndexOf(mRunningColdStart) >= 0,
+ "isOrphaned " + mRunningColdStart);
+ }
- // Verify health of all known process queues
- for (int i = 0; i < mProcessQueues.size(); i++) {
- BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
- while (leaf != null) {
- leaf.checkHealthLocked();
- leaf = leaf.processNameNext;
- }
+ // Verify health of all known process queues
+ for (int i = 0; i < mProcessQueues.size(); i++) {
+ BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
+ while (leaf != null) {
+ leaf.assertHealthLocked();
+ leaf = leaf.processNameNext;
}
-
- // If no health issues found above, check again in the future
- mLocalHandler.sendEmptyMessageDelayed(MSG_CHECK_HEALTH, DateUtils.MINUTE_IN_MILLIS);
-
- } catch (Exception e) {
- // Throw up a message to indicate that something went wrong, and
- // dump current state for later inspection
- Slog.wtf(TAG, e);
- dumpToDropBoxLocked(e.toString());
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9c1546324e4b..78edbba2e569 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
+import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FREEZER;
@@ -27,12 +28,14 @@ import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
+import android.app.IApplicationThread;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
@@ -1231,6 +1234,17 @@ public final class CachedAppOptimizer {
return;
}
+ if (mAm.mConstants.USE_MODERN_TRIM
+ && app.mState.getSetAdj() >= ProcessList.CACHED_APP_MIN_ADJ) {
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ try {
+ thread.scheduleTrimMemory(TRIM_MEMORY_BACKGROUND);
+ } catch (RemoteException e) {
+ // do nothing
+ }
+ }
+ }
mFreezeHandler.sendMessageDelayed(
mFreezeHandler.obtainMessage(
SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index eccee52f37aa..067a2d6d8b0d 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -16,7 +16,10 @@
package com.android.server.display;
+import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
@@ -63,8 +66,16 @@ class BrightnessThrottler {
private final DeviceConfigInterface mDeviceConfig;
private int mThrottlingStatus;
+
+ // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
+ @NonNull
+ private HashMap<String, BrightnessThrottlingData> mDdcThrottlingDataMap;
+
+ // Current throttling data being used.
+ // Null if we do not support throttling.
+ @Nullable
private BrightnessThrottlingData mThrottlingData;
- private BrightnessThrottlingData mDdcThrottlingData;
+
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -73,35 +84,45 @@ class BrightnessThrottler {
// The most recent string that has been set from DeviceConfig
private String mBrightnessThrottlingDataString;
+ // The brightness throttling configuration that should be used.
+ private String mBrightnessThrottlingDataId;
+
// This is a collection of brightness throttling data that has been written as overrides from
// the DeviceConfig. This will always take priority over the display device config data.
- private HashMap<String, BrightnessThrottlingData> mBrightnessThrottlingDataOverride =
- new HashMap<>(1);
-
- BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData,
- Runnable throttlingChangeCallback, String uniqueDisplayId) {
- this(new Injector(), handler, handler, throttlingData, throttlingChangeCallback,
- uniqueDisplayId);
+ // We need to store the data for every display device, so we do not need to update this each
+ // time the underlying display device changes.
+ // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData.
+ // HashMap< uniqueDisplayId, HashMap< throttlingDataId, BrightnessThrottlingData >>
+ private final HashMap<String, HashMap<String, BrightnessThrottlingData>>
+ mBrightnessThrottlingDataOverride = new HashMap<>(1);
+
+ BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
+ String throttlingDataId,
+ @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) {
+ this(new Injector(), handler, handler, throttlingChangeCallback,
+ uniqueDisplayId, throttlingDataId, brightnessThrottlingDataMap);
}
@VisibleForTesting
BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
- BrightnessThrottlingData throttlingData, Runnable throttlingChangeCallback,
- String uniqueDisplayId) {
+ Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
+ @NonNull HashMap<String, BrightnessThrottlingData> brightnessThrottlingDataMap) {
mInjector = injector;
mHandler = handler;
mDeviceConfigHandler = deviceConfigHandler;
- mThrottlingData = throttlingData;
- mDdcThrottlingData = throttlingData;
+ mDdcThrottlingDataMap = brightnessThrottlingDataMap;
mThrottlingChangeCallback = throttlingChangeCallback;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mUniqueDisplayId = uniqueDisplayId;
mDeviceConfig = injector.getDeviceConfig();
mDeviceConfigListener = new DeviceConfigListener();
-
- resetThrottlingData(mThrottlingData, mUniqueDisplayId);
+ mBrightnessThrottlingDataId = throttlingDataId;
+ mDdcThrottlingDataMap = brightnessThrottlingDataMap;
+ loadBrightnessThrottlingDataFromDeviceConfig();
+ loadBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThrottlingDataMap,
+ mBrightnessThrottlingDataId, mUniqueDisplayId);
}
boolean deviceSupportsThrottling() {
@@ -133,23 +154,14 @@ class BrightnessThrottler {
mThrottlingStatus = THROTTLING_INVALID;
}
- private void resetThrottlingData() {
- resetThrottlingData(mDdcThrottlingData, mUniqueDisplayId);
- }
-
- void resetThrottlingData(BrightnessThrottlingData throttlingData, String displayId) {
- stop();
-
- mUniqueDisplayId = displayId;
- mDdcThrottlingData = throttlingData;
- mDeviceConfigListener.startListening();
- reloadBrightnessThrottlingDataOverride();
- mThrottlingData = mBrightnessThrottlingDataOverride.getOrDefault(mUniqueDisplayId,
- throttlingData);
-
- if (deviceSupportsThrottling()) {
- mSkinThermalStatusObserver.startObserving();
- }
+ void loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+ HashMap<String, BrightnessThrottlingData> ddcThrottlingDataMap,
+ String brightnessThrottlingDataId,
+ String uniqueDisplayId) {
+ mDdcThrottlingDataMap = ddcThrottlingDataMap;
+ mBrightnessThrottlingDataId = brightnessThrottlingDataId;
+ mUniqueDisplayId = uniqueDisplayId;
+ resetThrottlingData();
}
private float verifyAndConstrainBrightnessCap(float brightness) {
@@ -183,7 +195,7 @@ class BrightnessThrottler {
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
- if (mThrottlingStatus != THROTTLING_INVALID) {
+ if (mThrottlingStatus != THROTTLING_INVALID && mThrottlingData != null) {
// Throttling levels are sorted by increasing severity
for (ThrottlingLevel level : mThrottlingData.throttlingLevels) {
if (level.thermalStatus <= mThrottlingStatus) {
@@ -218,13 +230,14 @@ class BrightnessThrottler {
private void dumpLocal(PrintWriter pw) {
pw.println("BrightnessThrottler:");
+ pw.println(" mBrightnessThrottlingDataId=" + mBrightnessThrottlingDataId);
pw.println(" mThrottlingData=" + mThrottlingData);
- pw.println(" mDdcThrottlingData=" + mDdcThrottlingData);
pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
pw.println(" mThrottlingStatus=" + mThrottlingStatus);
pw.println(" mBrightnessCap=" + mBrightnessCap);
pw.println(" mBrightnessMaxReason=" +
BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+ pw.println(" mDdcThrottlingDataMap=" + mDdcThrottlingDataMap);
pw.println(" mBrightnessThrottlingDataOverride=" + mBrightnessThrottlingDataOverride);
pw.println(" mBrightnessThrottlingDataString=" + mBrightnessThrottlingDataString);
@@ -237,8 +250,18 @@ class BrightnessThrottler {
/* defaultValue= */ null);
}
- private boolean parseAndSaveData(@NonNull String strArray,
- @NonNull HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData) {
+ // The brightness throttling data id may or may not be specified in the string that is passed
+ // in, if there is none specified, we assume it is for the default case. Each string passed in
+ // here must be for one display and one throttling id.
+ // 123,1,critical,0.8
+ // 456,2,moderate,0.9,critical,0.7
+ // 456,2,moderate,0.9,critical,0.7,default
+ // 456,2,moderate,0.9,critical,0.7,id_2
+ // displayId, number, <state, val> * number
+ // displayId, <number, <state, val> * number>, throttlingId
+ private boolean parseAndAddData(@NonNull String strArray,
+ @NonNull HashMap<String, HashMap<String, BrightnessThrottlingData>>
+ displayIdToThrottlingIdToBtd) {
boolean validConfig = true;
String[] items = strArray.split(",");
int i = 0;
@@ -254,29 +277,42 @@ class BrightnessThrottler {
for (int j = 0; j < noOfThrottlingPoints; j++) {
String severity = items[i++];
int status = parseThermalStatus(severity);
-
float brightnessPoint = parseBrightness(items[i++]);
-
throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint));
}
- BrightnessThrottlingData toSave =
+
+ String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID;
+ BrightnessThrottlingData throttlingLevelsData =
DisplayDeviceConfig.BrightnessThrottlingData.create(throttlingLevels);
- tempBrightnessThrottlingData.put(uniqueDisplayId, toSave);
+
+ // Add throttlingLevelsData to inner map where necessary.
+ HashMap<String, BrightnessThrottlingData> throttlingMapForDisplay =
+ displayIdToThrottlingIdToBtd.get(uniqueDisplayId);
+ if (throttlingMapForDisplay == null) {
+ throttlingMapForDisplay = new HashMap<>();
+ throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
+ displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay);
+ } else if (throttlingMapForDisplay.containsKey(throttlingDataId)) {
+ Slog.e(TAG, "Throttling data for display " + uniqueDisplayId
+ + "contains duplicate throttling ids: '" + throttlingDataId + "'");
+ return false;
+ } else {
+ throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
+ }
} catch (NumberFormatException | IndexOutOfBoundsException
| UnknownThermalStatusException e) {
- validConfig = false;
Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e);
+ validConfig = false;
}
if (i != items.length) {
validConfig = false;
}
-
return validConfig;
}
- public void reloadBrightnessThrottlingDataOverride() {
- HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData =
+ private void loadBrightnessThrottlingDataFromDeviceConfig() {
+ HashMap<String, HashMap<String, BrightnessThrottlingData>> tempThrottlingData =
new HashMap<>(1);
mBrightnessThrottlingDataString = getBrightnessThrottlingDataString();
boolean validConfig = true;
@@ -284,15 +320,15 @@ class BrightnessThrottler {
if (mBrightnessThrottlingDataString != null) {
String[] throttlingDataSplits = mBrightnessThrottlingDataString.split(";");
for (String s : throttlingDataSplits) {
- if (!parseAndSaveData(s, tempBrightnessThrottlingData)) {
+ if (!parseAndAddData(s, tempThrottlingData)) {
validConfig = false;
break;
}
}
if (validConfig) {
- mBrightnessThrottlingDataOverride.putAll(tempBrightnessThrottlingData);
- tempBrightnessThrottlingData.clear();
+ mBrightnessThrottlingDataOverride.putAll(tempThrottlingData);
+ tempThrottlingData.clear();
}
} else {
@@ -300,15 +336,50 @@ class BrightnessThrottler {
}
}
+ private void resetThrottlingData() {
+ stop();
+
+ mDeviceConfigListener.startListening();
+
+ // Get throttling data for this id, if it exists
+ mThrottlingData = getConfigFromId(mBrightnessThrottlingDataId);
+
+ // Fallback to default id otherwise.
+ if (!DEFAULT_ID.equals(mBrightnessThrottlingDataId) && mThrottlingData == null) {
+ mThrottlingData = getConfigFromId(DEFAULT_ID);
+ Slog.d(TAG, "Falling back to default throttling Id");
+ }
+
+ if (deviceSupportsThrottling()) {
+ mSkinThermalStatusObserver.startObserving();
+ }
+ }
+
+ private BrightnessThrottlingData getConfigFromId(String id) {
+ BrightnessThrottlingData returnValue;
+
+ // Fallback pattern for fetching correct throttling data for this display and id.
+ // 1) throttling data from device config for this throttling data id
+ returnValue = mBrightnessThrottlingDataOverride.get(mUniqueDisplayId) == null
+ ? null
+ : mBrightnessThrottlingDataOverride.get(mUniqueDisplayId).get(id);
+ // 2) throttling data from ddc for this throttling data id
+ returnValue = returnValue == null
+ ? mDdcThrottlingDataMap.get(id)
+ : returnValue;
+
+ return returnValue;
+ }
+
/**
* Listens to config data change and updates the brightness throttling data using
* DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA.
* The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe,
* 0.379518072;local:4619827677550801151,1,moderate,0.75"
* In this order:
- * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
- * Where the latter part is repeated for each throttling level, and the entirety is repeated
- * for each display, separated by a semicolon.
+ * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>][,throttlingId]?
+ * Where [<severity as string>,<brightness cap>] is repeated for each throttling level, and the
+ * entirety is repeated for each display & throttling data id, separated by a semicolon.
*/
public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
@@ -320,7 +391,7 @@ class BrightnessThrottler {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- reloadBrightnessThrottlingDataOverride();
+ loadBrightnessThrottlingDataFromDeviceConfig();
resetThrottlingData();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index f4b3f1ab7b11..da021158eb65 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -143,17 +143,17 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <brightness>0.01</brightness>
* </brightnessThrottlingPoint>
* </brightnessThrottlingMap>
- * <concurrentDisplaysBrightnessThrottlingMap>
- * <brightnessThrottlingPoint>
- * <thermalStatus>severe</thermalStatus>
- * <brightness>0.07</brightness>
- * </brightnessThrottlingPoint>
- * <brightnessThrottlingPoint>
- * <thermalStatus>critical</thermalStatus>
- * <brightness>0.005</brightness>
- * </brightnessThrottlingPoint>
- * </concurrentDisplaysBrightnessThrottlingMap>
- * <refreshRateThrottlingMap>
+ * <brightnessThrottlingMap id="id_2"> // optional attribute, leave blank for default
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>moderate</thermalStatus>
+ * <brightness>0.2</brightness>
+ * </brightnessThrottlingPoint>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <brightness>0.1</brightness>
+ * </brightnessThrottlingPoint>
+ * </brightnessThrottlingMap>
+ <refreshRateThrottlingMap>
* <refreshRateThrottlingPoint>
* <thermalStatus>critical</thermalStatus>
* <refreshRateRange>
@@ -687,8 +687,8 @@ public class DisplayDeviceConfig {
private int[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
private int[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
- private final Map<String, BrightnessThrottlingData> mBrightnessThrottlingDataMap =
- new HashMap<>();
+ private final HashMap<String, BrightnessThrottlingData>
+ mBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
mRefreshRateThrottlingMap = new HashMap<>();
@@ -1346,11 +1346,11 @@ public class DisplayDeviceConfig {
}
/**
- * @param id The ID of the throttling data
- * @return brightness throttling configuration data for the display.
+ * @return brightness throttling configuration data for this display, for each throttling id.
*/
- public BrightnessThrottlingData getBrightnessThrottlingData(String id) {
- return BrightnessThrottlingData.create(mBrightnessThrottlingDataMap.get(id));
+ public HashMap<String, BrightnessThrottlingData>
+ getBrightnessThrottlingDataMapByThrottlingId() {
+ return mBrightnessThrottlingDataMapByThrottlingId;
}
/**
@@ -1525,7 +1525,8 @@ public class DisplayDeviceConfig {
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ ", mHbmData=" + mHbmData
+ ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
- + ", mBrightnessThrottlingData=" + mBrightnessThrottlingDataMap
+ + ", mBrightnessThrottlingDataMapByThrottlingId="
+ + mBrightnessThrottlingDataMapByThrottlingId
+ "\n"
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
@@ -1918,11 +1919,11 @@ public class DisplayDeviceConfig {
if (!badConfig) {
String id = map.getId() == null ? DEFAULT_ID
: map.getId();
- if (mBrightnessThrottlingDataMap.containsKey(id)) {
+ if (mBrightnessThrottlingDataMapByThrottlingId.containsKey(id)) {
throw new RuntimeException("Brightness throttling data with ID " + id
+ " already exists");
}
- mBrightnessThrottlingDataMap.put(id,
+ mBrightnessThrottlingDataMapByThrottlingId.put(id,
BrightnessThrottlingData.create(throttlingLevels));
}
}
@@ -1971,8 +1972,8 @@ public class DisplayDeviceConfig {
));
}
if (refreshRates.size() == 0) {
- Slog.w(TAG, "RefreshRateThrottling: no valid throttling points fond for map, mapId="
- + id);
+ Slog.w(TAG, "RefreshRateThrottling: no valid throttling points found for map, "
+ + "mapId=" + id);
continue;
}
mRefreshRateThrottlingMap.put(id, refreshRates);
@@ -3077,7 +3078,7 @@ public class DisplayDeviceConfig {
/**
- * Creates multiple teperature based throttling levels of brightness
+ * Creates multiple temperature based throttling levels of brightness
*/
public static BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels) {
if (throttlingLevels == null || throttlingLevels.size() == 0) {
@@ -3120,15 +3121,6 @@ public class DisplayDeviceConfig {
return new BrightnessThrottlingData(throttlingLevels);
}
- static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
- if (other == null) {
- return null;
- }
-
- return BrightnessThrottlingData.create(other.throttlingLevels);
- }
-
-
@Override
public String toString() {
return "BrightnessThrottlingData{"
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f5859eed34f1..2322e03d6d37 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -505,7 +505,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private DisplayDeviceConfig mDisplayDeviceConfig;
- // Identifiers for suspend blocker acuisition requests
+ // Identifiers for suspend blocker acquisition requests
private final String mSuspendBlockerIdUnfinishedBusiness;
private final String mSuspendBlockerIdOnStateChanged;
private final String mSuspendBlockerIdProxPositive;
@@ -515,6 +515,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private boolean mIsEnabled;
private boolean mIsInTransition;
+ // The id of the brightness throttling policy that should be used.
private String mBrightnessThrottlingDataId;
// DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
@@ -905,14 +906,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
loadNitBasedBrightnessSetting();
/// Since the underlying display-device changed, we really don't know the
- // last command that was sent to change it's state. Lets assume it is unknown so
+ // last command that was sent to change it's state. Let's assume it is unknown so
// that we trigger a change immediately.
mPowerState.resetScreenState();
} else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) {
changed = true;
mBrightnessThrottlingDataId = brightnessThrottlingDataId;
- mBrightnessThrottler.resetThrottlingData(
- config.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
+ mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+ config.getBrightnessThrottlingDataMapByThrottlingId(),
+ mBrightnessThrottlingDataId,
mUniqueDisplayId);
}
if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
@@ -981,9 +983,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
sdrBrightness, maxDesiredHdrSdrRatio);
}
});
- mBrightnessThrottler.resetThrottlingData(
- mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
- mUniqueDisplayId);
+ mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+ mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(),
+ mBrightnessThrottlingDataId, mUniqueDisplayId);
}
private void sendUpdatePowerState() {
@@ -2116,11 +2118,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
return new BrightnessThrottler(mHandler,
- ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
() -> {
sendUpdatePowerState();
postBrightnessChangeRunnable();
- }, mUniqueDisplayId);
+ }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(),
+ ddConfig.getBrightnessThrottlingDataMapByThrottlingId());
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 8ce4b66eba28..d2af88b1c6c9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -400,6 +400,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private boolean mIsEnabled;
private boolean mIsInTransition;
+ // The id of the brightness throttling policy that should be used.
private String mBrightnessThrottlingDataId;
// DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
@@ -722,14 +723,15 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
// Since the underlying display-device changed, we really don't know the
- // last command that was sent to change it's state. Lets assume it is unknown so
+ // last command that was sent to change it's state. Let's assume it is unknown so
// that we trigger a change immediately.
mPowerState.resetScreenState();
} else if (!mBrightnessThrottlingDataId.equals(brightnessThrottlingDataId)) {
changed = true;
mBrightnessThrottlingDataId = brightnessThrottlingDataId;
- mBrightnessThrottler.resetThrottlingData(
- config.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
+ mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+ config.getBrightnessThrottlingDataMapByThrottlingId(),
+ mBrightnessThrottlingDataId,
mUniqueDisplayId);
}
if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
@@ -795,9 +797,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
sdrBrightness, maxDesiredHdrSdrRatio);
}
});
- mBrightnessThrottler.resetThrottlingData(
- mDisplayDeviceConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
- mUniqueDisplayId);
+ mBrightnessThrottler.loadBrightnessThrottlingDataFromDisplayDeviceConfig(
+ mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId(),
+ mBrightnessThrottlingDataId, mUniqueDisplayId);
}
private void sendUpdatePowerState() {
@@ -1757,11 +1759,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
return new BrightnessThrottler(mHandler,
- ddConfig.getBrightnessThrottlingData(mBrightnessThrottlingDataId),
() -> {
sendUpdatePowerState();
postBrightnessChangeRunnable();
- }, mUniqueDisplayId);
+ }, mUniqueDisplayId, mLogicalDisplay.getBrightnessThrottlingDataIdLocked(),
+ ddConfig.getBrightnessThrottlingDataMapByThrottlingId());
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index f86ee249408c..634f31d6268c 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -338,7 +338,9 @@ public class Layout {
}
/**
- * @return The ID of the brightness throttling map that this display should use.
+ * Gets the id of the brightness throttling map that should be used.
+ * @return The ID of the brightness throttling map that this display should use, null if
+ * unspecified, will fall back to default.
*/
public String getBrightnessThrottlingMapId() {
return mBrightnessThrottlingMapId;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 84bee50b77b0..402fb30437b0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -25,6 +26,8 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.checkCallingOrSelfPermissionForPreflight;
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
@@ -32,6 +35,7 @@ import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -90,6 +94,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+import android.window.IDumpCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -104,6 +109,15 @@ import com.android.server.SystemService;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -111,6 +125,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
@@ -118,6 +133,15 @@ import java.util.concurrent.ExecutionException;
* managed profiles.
*/
public class LauncherAppsService extends SystemService {
+ private static final String WM_TRACE_DIR = "/data/misc/wmtrace/";
+ private static final String VC_FILE_SUFFIX = ".vc";
+
+ private static final Set<PosixFilePermission> WM_TRACE_FILE_PERMISSIONS = Set.of(
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.OTHERS_READ,
+ PosixFilePermission.OWNER_READ
+ );
private final LauncherAppsImpl mLauncherAppsImpl;
@@ -191,6 +215,8 @@ public class LauncherAppsService extends SystemService {
final LauncherAppsServiceInternal mInternal;
+ private RemoteCallbackList<IDumpCallback> mDumpCallbacks = new RemoteCallbackList<>();
+
public LauncherAppsImpl(Context context) {
mContext = context;
mIPM = AppGlobals.getPackageManager();
@@ -1431,6 +1457,66 @@ public class LauncherAppsService extends SystemService {
getActivityOptionsForLauncher(opts), user.getIdentifier());
}
+
+ /**
+ * Using a pipe, outputs view capture data to the wmtrace dir
+ */
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+
+ // Before the wmtrace directory is picked up by dumpstate service, some processes need
+ // to write their data to that location. They can do that via these dumpCallbacks.
+ int i = mDumpCallbacks.beginBroadcast();
+ while (i > 0) {
+ i--;
+ dumpDataToWmTrace((String) mDumpCallbacks.getBroadcastCookie(i) + "_" + i,
+ mDumpCallbacks.getBroadcastItem(i));
+ }
+ mDumpCallbacks.finishBroadcast();
+ }
+
+ private void dumpDataToWmTrace(String name, IDumpCallback cb) {
+ ParcelFileDescriptor[] pipe;
+ try {
+ pipe = ParcelFileDescriptor.createPipe();
+ cb.onDump(pipe[1]);
+ } catch (IOException | RemoteException e) {
+ Log.d(TAG, "failed to pipe view capture data", e);
+ return;
+ }
+
+ Path path = Paths.get(WM_TRACE_DIR + Paths.get(name + VC_FILE_SUFFIX).getFileName());
+ try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0])) {
+ Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING);
+ Files.setPosixFilePermissions(path, WM_TRACE_FILE_PERMISSIONS);
+ } catch (IOException e) {
+ Log.d(TAG, "failed to write data to file in wmtrace dir", e);
+ }
+ }
+
+ @RequiresPermission(READ_FRAME_BUFFER)
+ @Override
+ public void registerDumpCallback(IDumpCallback cb) {
+ int status = checkCallingOrSelfPermissionForPreflight(mContext, READ_FRAME_BUFFER);
+ if (PERMISSION_GRANTED == status) {
+ String name = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+ mDumpCallbacks.register(cb, name);
+ } else {
+ Log.w(TAG, "caller lacks permissions to registerDumpCallback");
+ }
+ }
+
+ @RequiresPermission(READ_FRAME_BUFFER)
+ @Override
+ public void unRegisterDumpCallback(IDumpCallback cb) {
+ int status = checkCallingOrSelfPermissionForPreflight(mContext, READ_FRAME_BUFFER);
+ if (PERMISSION_GRANTED == status) {
+ mDumpCallbacks.unregister(cb);
+ } else {
+ Log.w(TAG, "caller lacks permissions to unRegisterDumpCallback");
+ }
+ }
+
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ea6383e14969..048391eea292 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -745,6 +745,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
+ @GuardedBy("mLock")
+ private boolean mAllowsUpdateOwnership = true;
+
private static final FileFilter sAddedApkFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -866,13 +869,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final int USER_ACTION_NOT_NEEDED = 0;
private static final int USER_ACTION_REQUIRED = 1;
- private static final int USER_ACTION_PENDING_APK_PARSING = 2;
private static final int USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER = 3;
@IntDef({
USER_ACTION_NOT_NEEDED,
USER_ACTION_REQUIRED,
- USER_ACTION_PENDING_APK_PARSING,
USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER,
})
@interface UserActionRequirement {}
@@ -963,11 +964,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
&& !isApexSession()
&& !isUpdateOwner
&& !isInstallerShell
+ && mAllowsUpdateOwnership
// We don't enforce the update ownership for the managed user and profile.
&& !isFromManagedUserOrProfile) {
return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER;
}
-
if (isPermissionGranted) {
return USER_ACTION_NOT_NEEDED;
}
@@ -982,7 +983,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
&& isUpdateWithoutUserActionPermissionGranted
&& ((isUpdateOwnershipEnforcementEnabled ? isUpdateOwner
: isInstallerOfRecord) || isSelfUpdate)) {
- return USER_ACTION_PENDING_APK_PARSING;
+ if (!isApexSession()) {
+ if (!isTargetSdkConditionSatisfied(this)) {
+ return USER_ACTION_REQUIRED;
+ }
+
+ if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
+ getInstallerPackageName(), getPackageName())) {
+ // Fall back to the non-silent update if a repeated installation is invoked
+ // within the throttle time.
+ return USER_ACTION_REQUIRED;
+ }
+ mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
+ return USER_ACTION_NOT_NEEDED;
+ }
}
return USER_ACTION_REQUIRED;
@@ -2363,26 +2377,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
session.sendPendingUserActionIntent(target);
return true;
}
-
- if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
- if (!isTargetSdkConditionSatisfied(session)) {
- session.sendPendingUserActionIntent(target);
- return true;
- }
-
- if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
- if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
- session.getInstallerPackageName(), session.getPackageName())) {
- // Fall back to the non-silent update if a repeated installation is invoked
- // within the throttle time.
- session.sendPendingUserActionIntent(target);
- return true;
- }
- session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
- session.getPackageName());
- }
- }
-
return false;
}
@@ -3393,6 +3387,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// {@link PackageLite#getTargetSdk()}
mValidatedTargetSdk = packageLite.getTargetSdk();
+ mAllowsUpdateOwnership = packageLite.isAllowUpdateOwnership();
+
return packageLite;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6b213b78f11c..e4e3a9d0b7d3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7273,7 +7273,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final long token = Binder.clearCallingIdentity();
try {
return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE,
- PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, false /* defaultValue */);
+ PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, true /* defaultValue */);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index cc60802967b0..89f46fed4862 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -692,7 +692,7 @@ class PackageManagerShellCommand extends ShellCommand {
null /* usesSplitNames */, null /* configForSplit */,
null /* splitApkPaths */, null /* splitRevisionCodes */,
apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */,
- null /* splitTypes */);
+ null /* splitTypes */, apkLite.isAllowUpdateOwnership());
sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index b7a2b86b1bcd..a814ca46fa5e 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -136,6 +136,7 @@ public final class UserTypeFactory {
com.android.internal.R.color.system_neutral2_900)
.setDefaultRestrictions(null)
.setDefaultCrossProfileIntentFilters(getDefaultCloneCrossProfileIntentFilter())
+ .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
@@ -216,7 +217,8 @@ public final class UserTypeFactory {
com.android.internal.R.color.profile_badge_1_dark,
com.android.internal.R.color.profile_badge_2_dark,
com.android.internal.R.color.profile_badge_3_dark)
- .setDefaultRestrictions(restrictions);
+ .setDefaultRestrictions(restrictions)
+ .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings());
}
/**
@@ -337,6 +339,15 @@ public final class UserTypeFactory {
return DefaultCrossProfileIntentFiltersUtils.getDefaultCloneProfileFilters();
}
+ /** Gets a default bundle, keyed by Settings.Secure String names, for non-managed profiles. */
+ private static Bundle getDefaultNonManagedProfileSecureSettings() {
+ final Bundle settings = new Bundle();
+ // Non-managed profiles go through neither SetupWizard nor DPC flows, so we automatically
+ // mark them as setup.
+ settings.putString(android.provider.Settings.Secure.USER_SETUP_COMPLETE, "1");
+ return settings;
+ }
+
/**
* Reads the given xml parser to obtain device user-type customization, and updates the given
* map of {@link UserTypeDetails.Builder}s accordingly.
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index c2ddb4158846..00299c286b08 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -148,7 +148,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
@ScreenOrientation
int getOrientation(int candidate) {
final int orientation = super.getOrientation(candidate);
- if (getIgnoreOrientationRequest(orientation)) {
+ if (shouldIgnoreOrientationRequest(orientation)) {
// In all the other case, mLastOrientationSource will be reassigned to a new value
mLastOrientationSource = null;
return SCREEN_ORIENTATION_UNSET;
@@ -158,7 +158,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
@Override
boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
- return !getIgnoreOrientationRequest(orientation)
+ return !shouldIgnoreOrientationRequest(orientation)
&& super.handlesOrientationChangeFromDescendant(orientation);
}
@@ -169,7 +169,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
final int orientation = requestingContainer != null
? requestingContainer.getOverrideOrientation()
: SCREEN_ORIENTATION_UNSET;
- return !getIgnoreOrientationRequest(orientation)
+ return !shouldIgnoreOrientationRequest(orientation)
&& super.onDescendantOrientationChanged(requestingContainer);
}
@@ -236,8 +236,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
/**
* @return {@value true} if we need to ignore the orientation in input.
*/
- // TODO(b/262366204): Rename getIgnoreOrientationRequest to shouldIgnoreOrientationRequest
- boolean getIgnoreOrientationRequest(@ScreenOrientation int orientation) {
+ boolean shouldIgnoreOrientationRequest(@ScreenOrientation int orientation) {
// We always respect orientation request for ActivityInfo.SCREEN_ORIENTATION_LOCKED
// ActivityInfo.SCREEN_ORIENTATION_NOSENSOR.
// Main use case why this is important is Camera apps that rely on those
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ab109fcaa673..bec58b848478 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1648,7 +1648,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
boolean handlesOrientationChangeFromDescendant(@ScreenOrientation int orientation) {
- return !getIgnoreOrientationRequest(orientation)
+ return !shouldIgnoreOrientationRequest(orientation)
&& !getDisplayRotation().isFixedToUserRotation();
}
@@ -1752,7 +1752,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
final int activityOrientation = r.getOverrideOrientation();
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
- || getIgnoreOrientationRequest(activityOrientation)) {
+ || shouldIgnoreOrientationRequest(activityOrientation)) {
return ROTATION_UNDEFINED;
}
if (activityOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
@@ -5149,7 +5149,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@ScreenOrientation
int getOrientation(@ScreenOrientation int candidate) {
// IME does not participate in orientation.
- return getIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate;
+ return shouldIgnoreOrientationRequest(candidate) ? SCREEN_ORIENTATION_UNSET : candidate;
}
@Override
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index d65f464590c1..44d67687e260 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -292,7 +292,9 @@ class EmbeddedWindowController {
private void handleTap(boolean grantFocus) {
if (mInputChannel != null) {
if (mHostWindowState != null) {
- mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient,
+ // Use null session since this is being granted by system server and doesn't
+ // require the host session to be passed in
+ mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient,
mFocusGrantToken, grantFocus);
if (grantFocus) {
// If granting focus to the embedded when tapped, we need to ensure the host
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 76759ba53f5a..b0a879e96dcf 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1883,7 +1883,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Only allow to specify orientation if this TDA is the last focused one on this logical
// display that can request orientation request.
return mDisplayContent.getOrientationRequestingTaskDisplayArea() == this
- && !getIgnoreOrientationRequest(orientation);
+ && !shouldIgnoreOrientationRequest(orientation);
}
void clearPreferredTopFocusableRootTask() {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0fe1f923e4e5..1fb353476592 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -33,8 +33,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -167,6 +165,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
private SurfaceControl.Transaction mStartTransaction = null;
private SurfaceControl.Transaction mFinishTransaction = null;
+ /** Used for failsafe clean-up to prevent leaks due to misbehaving player impls. */
+ private SurfaceControl.Transaction mCleanupTransaction = null;
+
/**
* Contains change infos for both participants and all remote-animatable ancestors. The
* ancestors can be the promotion candidates so their start-states need to be captured.
@@ -787,6 +788,24 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
/**
+ * Build a transaction that cleans-up transition-only surfaces (transition root and snapshots).
+ * This will ALWAYS be applied on transition finish just in-case
+ */
+ private static void buildCleanupTransaction(SurfaceControl.Transaction t, TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.getSnapshot() != null) {
+ t.reparent(c.getSnapshot(), null);
+ }
+ }
+ for (int i = info.getRootCount() - 1; i >= 0; --i) {
+ final SurfaceControl leash = info.getRoot(i).getLeash();
+ if (leash == null) continue;
+ t.reparent(leash, null);
+ }
+ }
+
+ /**
* Set whether this transition can start a pip-enter transition when finished. This is usually
* true, but gets set to false when recents decides that it wants to finish its animation but
* not actually finish its animation (yeah...).
@@ -863,6 +882,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (mStartTransaction != null) mStartTransaction.close();
if (mFinishTransaction != null) mFinishTransaction.close();
mStartTransaction = mFinishTransaction = null;
+ if (mCleanupTransaction != null) {
+ mCleanupTransaction.apply();
+ mCleanupTransaction = null;
+ }
if (mState < STATE_PLAYING) {
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
@@ -1286,6 +1309,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
buildFinishTransaction(mFinishTransaction, info);
+ mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
+ buildCleanupTransaction(mCleanupTransaction, info);
if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) {
mController.dispatchLegacyAppTransitionStarting(info, mStatusBarTransitionDelay);
try {
@@ -1385,6 +1410,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
ci.mSnapshot.release();
}
}
+ if (mCleanupTransaction != null) {
+ mCleanupTransaction.apply();
+ mCleanupTransaction = null;
+ }
}
/** The transition is ready to play. Make the start transaction show the surfaces. */
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index ae6eaf09bec7..90f30b567023 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -96,6 +96,8 @@ public final class CredentialManagerService
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
"Caller is missing WRITE_SECURE_SETTINGS permission";
+ private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
+ "enable_credential_manager";
private final Context mContext;
@@ -790,7 +792,7 @@ public final class CredentialManagerService
try {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_CREDENTIAL,
- CredentialManager.DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
+ DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
false);
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4a824e4c331c..b2156414b122 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -92,6 +92,8 @@ import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION;
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS;
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
+import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK;
+import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA;
import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY;
@@ -439,6 +441,7 @@ import android.util.AtomicFile;
import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -773,6 +776,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L;
+ /**
+ * Apps targetting U+ should now expect that attempts to grant sensor permissions without
+ * authorisation will result in a security exception.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS = 277035314L;
+
// Only add to the end of the list. Do not change or rearrange these values, that will break
// historical data. Do not use negative numbers or zero, logger only handles positive
// integers.
@@ -2819,6 +2830,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return doAdmin;
}
+ ActiveAdmin getDefaultDeviceOwnerLocked(@UserIdInt int userId) {
+ ensureLocked();
+ ComponentName doComponent = mOwners.getDeviceOwnerComponent();
+ if (mOwners.getDeviceOwnerType(doComponent.getPackageName()) == DEFAULT_DEVICE_OWNER) {
+ ActiveAdmin doAdmin = getUserData(userId).mAdminMap.get(doComponent);
+ return doAdmin;
+ }
+ return null;
+ }
+
ActiveAdmin getProfileOwnerLocked(@UserIdInt int userId) {
ensureLocked();
final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId);
@@ -2848,6 +2869,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return getDeviceOwnerLocked(userId);
}
+ ActiveAdmin getProfileOwnerOrDefaultDeviceOwnerLocked(@UserIdInt int userId) {
+ ensureLocked();
+ // Try to find an admin which can use reqPolicy
+ final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(userId);
+
+ if (poAdminComponent != null) {
+ return getProfileOwnerLocked(userId);
+ }
+
+ return getDefaultDeviceOwnerLocked(userId);
+ }
+
@NonNull ActiveAdmin getParentOfAdminIfRequired(ActiveAdmin admin, boolean parent) {
Objects.requireNonNull(admin);
return parent ? admin.getParentActiveAdmin() : admin;
@@ -5337,9 +5370,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
saveSettingsLocked(caller.getUserId());
});
+
+ //TODO(b/276855301): caller.getPackageName() will be null when the coexistence flags are
+ // turned off. Change back to caller.getPackageName once this API is unflagged.
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY)
- .setAdmin(caller.getPackageName())
+ .setAdmin(admin.info.getPackageName())
.setInt(passwordComplexity)
.setBoolean(calledOnParent)
.write();
@@ -5974,8 +6010,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public void lockNow(int flags, boolean parent) {
- final CallerIdentity caller = getCallerIdentity();
+ public void lockNow(int flags, String callerPackageName, boolean parent) {
+ CallerIdentity caller;
+ if (isPermissionCheckFlagEnabled()) {
+ caller = getCallerIdentity(callerPackageName);
+ } else {
+ caller = getCallerIdentity();
+ }
final int callingUserId = caller.getUserId();
ComponentName adminComponent = null;
@@ -5984,11 +6025,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Make sure the caller has any active admin with the right policy or
// the required permission.
if (isPermissionCheckFlagEnabled()) {
- admin = getActiveAdminOrCheckPermissionsForCallerLocked(
- null,
- DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
- parent,
- Set.of(MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE));
+ admin = enforcePermissionAndGetEnforcingAdmin(
+ /* admin= */ null,
+ /* permission= */ MANAGE_DEVICE_POLICY_LOCK,
+ USES_POLICY_FORCE_LOCK,
+ caller.getPackageName(),
+ getAffectedUser(parent)
+ ).getActiveAdmin();
} else {
admin = getActiveAdminOrCheckPermissionForCallerLocked(
null,
@@ -7481,7 +7524,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (isPolicyEngineForFinanceFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/*admin=*/ null,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
+ /*permission= */ MANAGE_DEVICE_POLICY_WIPE_DATA,
+ USES_POLICY_WIPE_DATA,
caller.getPackageName(),
factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
admin = enforcingAdmin.getActiveAdmin();
@@ -8511,7 +8555,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
isProfileOwnerOfOrganizationOwnedDevice(caller));
} else {
Preconditions.checkCallAuthorization(isProfileOwner(caller)
- || isDeviceOwner(caller));
+ || isDefaultDeviceOwner(caller));
}
}
@@ -8525,7 +8569,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
targetUserId).getActiveAdmin();
} else {
ap = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
+ getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent);
}
if (ap.disableScreenCapture != disabled) {
@@ -8557,15 +8601,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (admin != null && admin.disableScreenCapture) {
setScreenCaptureDisabled(UserHandle.USER_ALL);
- } else {
- // Otherwise, update screen capture only for the calling user.
- admin = getProfileOwnerAdminLocked(adminUserId);
- if (admin != null && admin.disableScreenCapture) {
- setScreenCaptureDisabled(adminUserId);
- } else {
- setScreenCaptureDisabled(UserHandle.USER_NULL);
- }
+ return;
+ }
+ // Otherwise, update screen capture only for the calling user.
+ admin = getProfileOwnerAdminLocked(adminUserId);
+ if (admin != null && admin.disableScreenCapture) {
+ setScreenCaptureDisabled(adminUserId);
+ return;
}
+ // If the admin is permission based, update only for the calling user.
+ admin = getUserData(adminUserId).createOrGetPermissionBasedAdmin(adminUserId);
+ if (admin != null && admin.disableScreenCapture) {
+ setScreenCaptureDisabled(adminUserId);
+ return;
+ }
+ setScreenCaptureDisabled(UserHandle.USER_NULL);
}
// Set the latest screen capture policy, overriding any existing ones.
@@ -16466,6 +16516,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
callerPackage,
caller.getUserId());
+ if (SENSOR_PERMISSIONS.contains(permission)
+ && grantState == PERMISSION_GRANT_STATE_GRANTED
+ && (!canAdminGrantSensorsPermissions() || isCallerDelegate(caller))) {
+ if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
+ caller.getPackageName(), caller.getUserId())) {
+ throw new SecurityException(
+ "Caller not permitted to grant sensor permissions.");
+ } else {
+ // This is to match the legacy behaviour.
+ callback.sendResult(Bundle.EMPTY);
+ return;
+ }
+ }
+ // Check all the states where Exceptions aren't thrown but the permission
+ // isn't granted either.
+ if (!canGrantPermission(caller, permission, packageName)) {
+ callback.sendResult(null);
+ return;
+ }
// TODO(b/266924257): decide how to handle the internal state if the package doesn't
// exist, or the permission isn't requested by the app, because we could end up with
// inconsistent state between the policy engine and package manager. Also a package
@@ -16541,6 +16610,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>();
+ {
+ SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+ SENSOR_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ SENSOR_PERMISSIONS.add(Manifest.permission.CAMERA);
+ SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
+ SENSOR_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
+ SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA);
+ SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO);
+ SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ SENSOR_PERMISSIONS.add(
+ Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
+ SENSOR_PERMISSIONS.add(
+ Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
+ }
+
+ private boolean canGrantPermission(CallerIdentity caller, String permission,
+ String targetPackageName) {
+ boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+ >= android.os.Build.VERSION_CODES.Q;
+ if (!isPostQAdmin) {
+ // Legacy admins assume that they cannot control pre-M apps
+ if (getTargetSdk(targetPackageName, caller.getUserId())
+ < android.os.Build.VERSION_CODES.M) {
+ return false;
+ }
+ }
+ if (!isRuntimePermission(permission)) {
+ return false;
+ }
+ return true;
+ }
+
private void enforcePermissionGrantStateOnFinancedDevice(
String packageName, String permission) {
if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) {
@@ -17637,7 +17741,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (getLockObject()) {
if (isPermissionCheckFlagEnabled()) {
- // TODO: add support for DELEGATION_SECURITY_LOGGING
enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
@@ -17718,7 +17821,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
if (isPermissionCheckFlagEnabled()) {
- // TODO: Restore the "affiliated users" check
+ Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+ || areAllUsersAffiliatedWithDeviceLocked());
enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
@@ -17770,7 +17874,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
if (isPermissionCheckFlagEnabled()) {
- // TODO: Restore the "affiliated users" check
+ Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+ || areAllUsersAffiliatedWithDeviceLocked());
+
enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
@@ -22321,244 +22427,208 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Permissions of existing DPC types.
private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
+ MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
- SET_TIME,
- SET_TIME_ZONE,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_WIFI,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- MANAGE_DEVICE_POLICY_MTE,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_CERTIFICATES,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- MANAGE_DEVICE_POLICY_CAMERA,
- MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
- MANAGE_DEVICE_POLICY_DEFAULT_SMS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_STATUS_BAR,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CALLS,
MANAGE_DEVICE_POLICY_CAMERA,
+ MANAGE_DEVICE_POLICY_CERTIFICATES,
+ MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+ MANAGE_DEVICE_POLICY_DEFAULT_SMS,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
MANAGE_DEVICE_POLICY_FUN,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+ MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
MANAGE_DEVICE_POLICY_LOCATION,
+ MANAGE_DEVICE_POLICY_LOCK,
MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
+ MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_MICROPHONE,
MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_MTE,
MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ MANAGE_DEVICE_POLICY_PACKAGE_STATE,
MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
MANAGE_DEVICE_POLICY_PRINTING,
- MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
MANAGE_DEVICE_POLICY_PROFILES,
MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+ MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+ MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
+ MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
+ MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
MANAGE_DEVICE_POLICY_SMS,
+ MANAGE_DEVICE_POLICY_STATUS_BAR,
+ MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+ MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
MANAGE_DEVICE_POLICY_TIME,
+ MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
MANAGE_DEVICE_POLICY_USERS,
MANAGE_DEVICE_POLICY_VPN,
MANAGE_DEVICE_POLICY_WALLPAPER,
MANAGE_DEVICE_POLICY_WIFI,
MANAGE_DEVICE_POLICY_WINDOWS,
- MANAGE_DEVICE_POLICY_APP_RESTRICTIONS
+ MANAGE_DEVICE_POLICY_WIPE_DATA,
+ SET_TIME,
+ SET_TIME_ZONE
);
private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_CALLS,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+ MANAGE_DEVICE_POLICY_FACTORY_RESET,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_USERS,
+ MANAGE_DEVICE_POLICY_KEYGUARD,
+ MANAGE_DEVICE_POLICY_LOCK_TASK,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS);
- private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+ MANAGE_DEVICE_POLICY_USERS,
+ MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS
+ );
+
+ /**
+ * All the permisisons granted to a profile owner.
+ */
+ private static final List<String> PROFILE_OWNER_PERMISSIONS =
List.of(
- MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
- SET_TIME,
- SET_TIME_ZONE,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_WIFI,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- MANAGE_DEVICE_POLICY_MTE,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+ MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
MANAGE_DEVICE_POLICY_AUTOFILL,
- MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CALLS,
- MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+ MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
MANAGE_DEVICE_POLICY_LOCATION,
+ MANAGE_DEVICE_POLICY_LOCK,
MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_MICROPHONE,
- MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
- MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ MANAGE_DEVICE_POLICY_PACKAGE_STATE,
MANAGE_DEVICE_POLICY_PRINTING,
MANAGE_DEVICE_POLICY_PROFILES,
MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
- MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+ MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+ MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
- MANAGE_DEVICE_POLICY_SMS,
+ MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_TIME,
MANAGE_DEVICE_POLICY_VPN,
- MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
- MANAGE_DEVICE_POLICY_DEFAULT_SMS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
- MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_WIFI,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- MANAGE_DEVICE_POLICY_MTE,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_CERTIFICATES);
- private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of(
- SET_TIME,
- SET_TIME_ZONE,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
- MANAGE_DEVICE_POLICY_BLUETOOTH,
- MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_FUN,
- MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
- MANAGE_DEVICE_POLICY_USERS,
- MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
- MANAGE_DEVICE_POLICY_SAFE_BOOT,
- MANAGE_DEVICE_POLICY_SMS,
- MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_WINDOWS,
- MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- MANAGE_DEVICE_POLICY_CAMERA,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_STATUS_BAR,
- MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
- MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS);
- private static final List<String> PROFILE_OWNER_PERMISSIONS = List.of(
- MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
- MANAGE_DEVICE_POLICY_AUTOFILL,
- MANAGE_DEVICE_POLICY_CALLS,
- MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
- MANAGE_DEVICE_POLICY_DISPLAY,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_LOCALE,
- MANAGE_DEVICE_POLICY_LOCATION,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
- MANAGE_DEVICE_POLICY_PRINTING,
- MANAGE_DEVICE_POLICY_PROFILES,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
- MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
- MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_VPN,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_APP_RESTRICTIONS
+ MANAGE_DEVICE_POLICY_WIPE_DATA
);
+ /**
+ * All the additional permissions granted to an organisation owned profile owner.
+ */
+ private static final List<String>
+ ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+ List.of(
+ MANAGE_DEVICE_POLICY_ACROSS_USERS,
+ MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_BLUETOOTH,
+ MANAGE_DEVICE_POLICY_CAMERA,
+ MANAGE_DEVICE_POLICY_CERTIFICATES,
+ MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
+ MANAGE_DEVICE_POLICY_DEFAULT_SMS,
+ MANAGE_DEVICE_POLICY_LOCALE,
+ MANAGE_DEVICE_POLICY_MICROPHONE,
+ MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_MTE,
+ MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+ MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+ MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
+ MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+ MANAGE_DEVICE_POLICY_SMS,
+ MANAGE_DEVICE_POLICY_SYSTEM_UPDATES,
+ MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+ MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
+ MANAGE_DEVICE_POLICY_WIFI,
+ SET_TIME,
+ SET_TIME_ZONE
+ );
+
+
+ private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
+ List.of(
+ MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
+ MANAGE_DEVICE_POLICY_BLUETOOTH,
+ MANAGE_DEVICE_POLICY_CAMERA,
+ MANAGE_DEVICE_POLICY_DISPLAY,
+ MANAGE_DEVICE_POLICY_FUN,
+ MANAGE_DEVICE_POLICY_LOCK_TASK,
+ MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+ MANAGE_DEVICE_POLICY_PRINTING,
+ MANAGE_DEVICE_POLICY_PROFILES,
+ MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+ MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_SMS,
+ MANAGE_DEVICE_POLICY_STATUS_BAR,
+ MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+ MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
+ MANAGE_DEVICE_POLICY_USERS,
+ MANAGE_DEVICE_POLICY_WINDOWS,
+ SET_TIME,
+ SET_TIME_ZONE
+ );
+
+ /**
+ * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+ * {@link ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS}.
+ */
+ private static final List<String> PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
+ new ArrayList();
+
+ /**
+ * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+ * {@link ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS}.
+ */
+ private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS =
+ new ArrayList();
+
+
private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
{
+ // Organisation owned profile owners have all the permission of a profile owner plus
+ // some extra permissions.
+ PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+ PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS.addAll(
+ ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
+ // Profile owners on user 0 have all the permission of a profile owner plus
+ // some extra permissions.
+ PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+ PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+
DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
DPC_PERMISSIONS.put(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
@@ -22566,14 +22636,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
}
-
- // Map of permission Active admin DEVICE_POLICY.
- //TODO(b/254253251) Fill this map in as new permissions are added for policies.
- private static final HashMap<String, Integer> ACTIVE_ADMIN_POLICIES = new HashMap<>();
- {
- //Any ActiveAdmin is able to call the support message APIs without certain policies.
- ACTIVE_ADMIN_POLICIES.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, null);
- }
//Map of Permission to Delegate Scope.
private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
{
@@ -22587,123 +22649,119 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
new HashMap<>();
{
- // Time and Timezone is intrinsically global so there is no cross-user permission.
- CROSS_USER_PERMISSIONS.put(SET_TIME, null);
- CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null);
- // system updates are intrinsically global so there is no cross-user permission
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null);
- // security logs are intrinsically global so there is no cross-user permission
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null);
- // usb signalling is intrinsically global so there is no cross-user permission
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null);
- // mte is intrinsically global so there is no cross-user permission
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null);
+ // The permissions are all intrinsically global and therefore have no cross-user permission.
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET, null);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MTE, null);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, null);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_STATUS_BAR, null);
- // Organisation identity policy will involve data of other organisations on the device and
- // therefore the FULL cross-user permission is required.
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_UPDATES, null);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, null);
+ CROSS_USER_PERMISSIONS.put(SET_TIME, null);
+ CROSS_USER_PERMISSIONS.put(SET_TIME_ZONE, null);
+
+ // The permissions are all critical for securing data within the current user and
+ // therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL for
+ // cross-user calls.
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_KEYGUARD,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(
- MANAGE_DEVICE_POLICY_LOCK, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(
- MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
- // Granting runtime permissions can grant applications significant powers therefore the FULL
- // cross-user permission is required.
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+
+ // These permissions are required for securing device ownership without accessing user data
+ // and therefore are protected with MANAGE_DEVICE_POLICY_ACROSS_USERS for cross-user calls.
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CALLS,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SMS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_TIME,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS);
+
+ // These permissions may grant access to user data and therefore must be protected with
+ // MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls.
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_FUN,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCALE,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCATION,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MOBILE_NETWORK,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING,
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SAFE_BOOT,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PRINTING,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SMS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USERS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_VPN,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WALLPAPER,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIFI,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WINDOWS,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEFAULT_SMS,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_ACROSS_USERS);
- CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
}
/**
@@ -22727,6 +22785,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ * Returns an {@link EnforcingAdmin} for the caller.
+ *
+ * @param admin the component name of the admin.
+ * @param callerPackageName The package name of the calling application.
+ * @param permission The name of the permission being checked.
+ * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private EnforcingAdmin enforcePermissionAndGetEnforcingAdmin(@Nullable ComponentName admin,
+ String permission, int deviceAdminPolicy, String callerPackageName, int targetUserId) {
+ enforcePermission(permission, deviceAdminPolicy, callerPackageName, targetUserId);
+ return getEnforcingAdminForCaller(admin, callerPackageName);
+ }
+
+ /**
* Checks whether the calling process has been granted permission to query a device policy on
* a specific user.
* The given permission will be checked along with its associated cross-user permission if it
@@ -22748,6 +22826,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
POLICY_IDENTIFIER_TO_PERMISSION.put(AUTO_TIMEZONE_POLICY, SET_TIME_ZONE);
}
+ private static final HashMap<String, Integer> POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY =
+ new HashMap<>();
+
/**
* Checks if the calling process has been granted permission to apply a device policy on a
* specific user.
@@ -22763,6 +22844,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void enforcePermission(String permission, String callerPackageName, int targetUserId)
throws SecurityException {
if (!hasPermission(permission, callerPackageName, targetUserId)) {
+ // TODO(b/276920002): Split the error messages so that the cross-user permission
+ // is only mentioned when it is needed.
+ throw new SecurityException("Caller does not have the required permissions for "
+ + "this user. Permissions required: {"
+ + permission
+ + ", "
+ + CROSS_USER_PERMISSIONS.get(permission)
+ + "(if calling cross-user)"
+ + "}");
+ }
+ }
+
+ /**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ *
+ * @param callerPackageName The package name of the calling application.
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private void enforcePermission(String permission, int adminPolicy,
+ String callerPackageName, int targetUserId)
+ throws SecurityException {
+ if (!hasPermissionOrAdminPolicy(permission, callerPackageName, adminPolicy, targetUserId)) {
+ // TODO(b/276920002): Split the error messages so that the cross-user permission
+ // is only mentioned when it is needed.
throw new SecurityException("Caller does not have the required permissions for "
+ "this user. Permissions required: {"
+ permission
@@ -22804,16 +22915,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
*/
private boolean hasPermission(String permission, String callerPackageName, int targetUserId) {
CallerIdentity caller = getCallerIdentity(callerPackageName);
- boolean hasPermissionOnOwnUser = hasPermission(permission, callerPackageName);
+ boolean hasPermissionOnOwnUser = hasPermission(permission, caller.getPackageName());
boolean hasPermissionOnTargetUser = true;
if (hasPermissionOnOwnUser & caller.getUserId() != targetUserId) {
hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission),
- callerPackageName);
+ caller.getPackageName());
}
return hasPermissionOnOwnUser && hasPermissionOnTargetUser;
}
+ private boolean hasPermissionOrAdminPolicy(String permission, String callerPackageName,
+ int adminPolicy, int targetUserId) {
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ if (hasPermission(permission, caller.getPackageName(), targetUserId)) {
+ return true;
+ }
+ ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller);
+ return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy);
+ }
+
/**
* Return whether the calling process has been granted the given permission.
*
@@ -22856,23 +22977,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (DELEGATE_SCOPES.containsKey(permission)) {
return isCallerDelegate(caller, DELEGATE_SCOPES.get(permission));
}
- // Check if the caller is an active admin that uses a certain policy.
- if (ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
- try {
- if (ACTIVE_ADMIN_POLICIES.get(permission) != null) {
- return getActiveAdminForCallerLocked(
- null, ACTIVE_ADMIN_POLICIES.get(permission), false) != null;
- } else {
- // If the permission maps to no policy (null) this means that any active admin
- // has permission.
- return isCallerActiveAdminOrDelegate(caller, null);
- }
- } catch (SecurityException e) {
- // A security exception means there is not an active admin with permission and
- // therefore
- return false;
- }
- }
return false;
}
@@ -22925,9 +23029,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (admin != null) {
return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
}
- if (admin == null) {
- admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
- }
+ admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
}
@@ -23476,15 +23578,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// We need to add a mapping of policyId to permission in POLICY_IDENTIFIER_TO_PERMISSION
// for each migrated permission.
private List<ActiveAdmin> getNonDPCActiveAdminsForPolicyLocked(String policyIdentifier) {
- String permission = POLICY_IDENTIFIER_TO_PERMISSION.get(policyIdentifier);
- if (permission == null) {
- Slogf.e(LOG_TAG, "Can't find a permission for %s in POLICY_IDENTIFIER_TO_PERMISSION",
+ Integer activeAdminPolicy = POLICY_IDENTIFIER_TO_ACTIVE_ADMIN_POLICY.get(policyIdentifier);
+ if (activeAdminPolicy == null) {
+ Slogf.e(LOG_TAG,
+ "Can't find a active admin policy for %s in POLICY_IDENTIFIER_TO_PERMISSION",
policyIdentifier);
return new ArrayList<>();
}
- if (!ACTIVE_ADMIN_POLICIES.containsKey(permission)) {
- return new ArrayList<>();
- }
List<ActiveAdmin> admins = new ArrayList<>();
for (UserInfo userInfo : mUserManager.getUsers()) {
@@ -23495,7 +23595,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
if (isActiveAdminWithPolicyForUserLocked(
- policy.mAdminMap.get(admin), ACTIVE_ADMIN_POLICIES.get(permission),
+ policy.mAdminMap.get(admin), activeAdminPolicy,
userInfo.id)) {
admins.add(policy.mAdminMap.get(admin));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 0c512d2a0bd3..318067ee8681 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -91,8 +91,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
+import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
@@ -596,7 +596,7 @@ public final class BroadcastQueueModernImplTest {
// about the actual output, just that we don't crash
queue.getActive().setDeliveryState(0, BroadcastRecord.DELIVERY_SCHEDULED, "Test-driven");
queue.dumpLocked(SystemClock.uptimeMillis(),
- new IndentingPrintWriter(new PrintWriter(new ByteArrayOutputStream())));
+ new IndentingPrintWriter(new PrintWriter(Writer.nullWriter())));
queue.makeActiveNextPending();
assertEquals(Intent.ACTION_LOCALE_CHANGED, queue.getActive().intent.getAction());
@@ -1166,6 +1166,11 @@ public final class BroadcastQueueModernImplTest {
List<Intent> intents) {
for (int i = 0; i < intents.size(); i++) {
queue.makeActiveNextPending();
+
+ // While we're here, give our health check some test coverage
+ queue.assertHealthLocked();
+ queue.dumpLocked(0L, new IndentingPrintWriter(Writer.nullWriter()));
+
final Intent actualIntent = queue.getActive().intent;
final Intent expectedIntent = intents.get(i);
final String errMsg = "actual=" + actualIntent + ", expected=" + expectedIntent
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index cbc259797c12..b6bc02a41c21 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -106,10 +107,10 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -231,6 +232,7 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
+ assertHealth();
final ProcessStartBehavior behavior = mNextProcessStartBehavior
.getAndSet(ProcessStartBehavior.SUCCESS);
if (behavior == ProcessStartBehavior.FAIL_NULL) {
@@ -462,6 +464,7 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleReceiver() for "
+ Arrays.toString(invocation.getArguments()));
+ assertHealth();
final Intent intent = invocation.getArgument(0);
final Bundle extras = invocation.getArgument(5);
mScheduledBroadcasts.add(makeScheduledBroadcast(r, intent));
@@ -483,6 +486,7 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
+ Arrays.toString(invocation.getArguments()));
+ assertHealth();
final Intent intent = invocation.getArgument(1);
final Bundle extras = invocation.getArgument(4);
final boolean ordered = invocation.getArgument(5);
@@ -600,6 +604,13 @@ public class BroadcastQueueTest {
BackgroundStartPrivileges.NONE, false, null);
}
+ private void assertHealth() {
+ if (mImpl == Impl.MODERN) {
+ // If this fails, it'll throw a clear reason message
+ ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
+ }
+ }
+
private static Map<String, Object> asMap(Bundle bundle) {
final Map<String, Object> map = new HashMap<>();
if (bundle != null) {
@@ -769,7 +780,7 @@ public class BroadcastQueueTest {
// about the actual output, just that we don't crash
mQueue.dumpDebug(new ProtoOutputStream(),
ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
- mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(new ByteArrayOutputStream()),
+ mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()),
null, 0, true, true, true, null, false);
mQueue.dumpToDropBoxLocked(TAG);
@@ -1166,7 +1177,7 @@ public class BroadcastQueueTest {
// about the actual output, just that we don't crash
mQueue.dumpDebug(new ProtoOutputStream(),
ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
- mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(new ByteArrayOutputStream()),
+ mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()),
null, 0, true, true, true, null, false);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index ffe2fec380a8..ce4b438470df 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -54,6 +54,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@SmallTest
@@ -122,8 +123,6 @@ public class BrightnessThrottlerTest {
BrightnessThrottlingData data;
data = BrightnessThrottlingData.create((List<ThrottlingLevel>)null);
assertEquals(data, null);
- data = BrightnessThrottlingData.create((BrightnessThrottlingData)null);
- assertEquals(data, null);
data = BrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
assertEquals(data, null);
data = BrightnessThrottlingData.create(unsortedThermalLevels);
@@ -146,7 +145,7 @@ public class BrightnessThrottlerTest {
}
@Test
- public void testThrottlingUnsupported() throws Exception {
+ public void testThrottlingUnsupported() {
final BrightnessThrottler throttler = createThrottlerUnsupported();
assertFalse(throttler.deviceSupportsThrottling());
@@ -307,37 +306,18 @@ public class BrightnessThrottlerTest {
verify(mThermalServiceMock).registerThermalEventListenerWithType(
mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.4f);
- // Set status too low to trigger throttling
- listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
- mTestLooper.dispatchAll();
- assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
- assertFalse(throttler.isThrottled());
-
- // Set status high enough to trigger throttling
- listener.notifyThrottling(getSkinTemp(level.thermalStatus));
- mTestLooper.dispatchAll();
- assertEquals(0.4f, throttler.getBrightnessCap(), 0f);
- assertTrue(throttler.isThrottled());
-
- // Update thresholds
- // This data is equivalent to the string "123,1,critical,0.8", passed below
- final ThrottlingLevel newLevel = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
- 0.8f);
// Set new (valid) data from device config
mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,0.8");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
- // Set status too low to trigger throttling
- listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus - 1));
- mTestLooper.dispatchAll();
- assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
- assertFalse(throttler.isThrottled());
-
- // Set status high enough to trigger throttling
- listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus));
- mTestLooper.dispatchAll();
- assertEquals(newLevel.brightness, throttler.getBrightnessCap(), 0f);
- assertTrue(throttler.isThrottled());
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,1,critical,0.75;123,1,critical,0.99,id_2");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.75f);
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,1,critical,0.8,default;123,1,critical,0.99,id_2");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
}
@Test public void testInvalidThrottlingStrings() throws Exception {
@@ -370,6 +350,18 @@ public class BrightnessThrottlerTest {
testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
mDeviceConfigFake.setBrightnessThrottlingData(""); // Invalid format
testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ // Invalid string format
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,default,1,critical,0.75,1,critical,0.99");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ // Invalid level string and number string
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,1,1,critical,0.75,id_2,1,critical,0.99");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ // Invalid format - (two default ids for same display)
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,1,critical,0.75,default;123,1,critical,0.99");
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
}
private void testThrottling(BrightnessThrottler throttler, IThermalEventListener listener,
@@ -472,13 +464,17 @@ public class BrightnessThrottlerTest {
}
private BrightnessThrottler createThrottlerUnsupported() {
- return new BrightnessThrottler(mInjectorMock, mHandler, mHandler, null, () -> {}, null);
+ return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
+ /* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
+ /* throttlingDataId= */ null, /* throttlingDataMap= */ new HashMap<>(1));
}
private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
assertNotNull(data);
+ HashMap<String, BrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+ throttlingDataMap.put("default", data);
return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
- data, () -> {}, "123");
+ () -> {}, "123", "default", throttlingDataMap);
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 9fd647bb0b90..ed0755949869 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -50,6 +50,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@SmallTest
@@ -186,50 +187,72 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(new int[]{-1, 10, 20, 30, 40},
mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
- List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
- new ArrayList();
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel>
+ defaultThrottlingLevels = new ArrayList<>();
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.4f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.3f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.2f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.1f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.05f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.025f
));
- assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
- mDisplayDeviceConfig.getBrightnessThrottlingData("default"));
- throttlingLevels.clear();
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.BrightnessThrottlingData defaultThrottlingData =
+ new DisplayDeviceConfig.BrightnessThrottlingData(defaultThrottlingLevels);
+
+ List<DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel>
+ concurrentThrottlingLevels = new ArrayList<>();
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 0.2f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 0.15f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 0.1f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 0.05f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 0.025f
));
- throttlingLevels.add(new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel(
DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 0.0125f
));
- assertEquals(new DisplayDeviceConfig.BrightnessThrottlingData(throttlingLevels),
- mDisplayDeviceConfig.getBrightnessThrottlingData("concurrent"));
+ DisplayDeviceConfig.BrightnessThrottlingData concurrentThrottlingData =
+ new DisplayDeviceConfig.BrightnessThrottlingData(concurrentThrottlingLevels);
+
+ HashMap<String, DisplayDeviceConfig.BrightnessThrottlingData> throttlingDataMap =
+ new HashMap<>(2);
+ throttlingDataMap.put("default", defaultThrottlingData);
+ throttlingDataMap.put("concurrent", concurrentThrottlingData);
+
+ assertEquals(throttlingDataMap,
+ mDisplayDeviceConfig.getBrightnessThrottlingDataMapByThrottlingId());
assertNotNull(mDisplayDeviceConfig.getHostUsiVersion());
assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2);
@@ -246,8 +269,7 @@ public final class DisplayDeviceConfigTest {
mDisplayDeviceConfig.getHdrBrightnessFromSdr(0.62f, 1.25f),
SMALL_DELTA);
-
- // Todo: Add asserts for BrightnessThrottlingData, DensityMapping,
+ // Todo: Add asserts for DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 0b6756d7c063..6bcda3fbcf43 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -178,6 +178,8 @@ public final class UserManagerTest {
UserHandle.of(userInfo.id));
assertThat(userContext.getSystemService(
UserManager.class).isMediaSharedWithParent()).isTrue();
+ assertThat(Settings.Secure.getInt(userContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0)).isEqualTo(1);
List<UserInfo> list = mUserManager.getUsers();
List<UserInfo> cloneUsers = list.stream().filter(
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index b2843d82a08a..de82854d42ee 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -422,7 +422,8 @@ public class DexMetadataHelperTest {
null /* splitNames */, null /* isFeatureSplits */, null /* usesSplitNames */,
null /* configForSplit */, null /* splitApkPaths */,
null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
- null /* requiredSplitTypes */, null /* splitTypes */);
+ null /* requiredSplitTypes */, null /* splitTypes */,
+ false /* allowUpdateOwnership */);
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
}