Merge "Add VDM APIs to support permission streaming" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a271d06..08a09e1 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -36,6 +37,7 @@
":hwui_flags_java_lib{.generated_srcjars}",
":display_flags_lib{.generated_srcjars}",
":android.multiuser.flags-aconfig-java{.generated_srcjars}",
+ ":android.app.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -327,3 +329,29 @@
aconfig_declarations: "android.multiuser.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Activity Manager
+aconfig_declarations {
+ name: "android.app.flags-aconfig",
+ package: "android.app",
+ srcs: ["core/java/android/app/activity_manager.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.flags-aconfig-java",
+ aconfig_declarations: "android.app.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Broadcast Radio
+aconfig_declarations {
+ name: "android.hardware.radio.flags-aconfig",
+ package: "android.hardware.radio",
+ srcs: ["core/java/android/hardware/radio/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.radio.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.radio.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 56a69b4..e5e0ad3 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -86,6 +86,9 @@
droidstubs {
name: "system-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -126,6 +129,9 @@
droidstubs {
name: "test-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -173,6 +179,9 @@
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
diff --git a/core/api/current.txt b/core/api/current.txt
index d5b18ef..955858b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4616,9 +4616,9 @@
public class ActivityManager {
method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap);
- method public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
+ method @FlaggedApi("android.app.app_start_info") public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
method public void appNotResponding(@NonNull String);
- method public void clearApplicationStartInfoCompletionListener();
+ method @FlaggedApi("android.app.app_start_info") public void clearApplicationStartInfoCompletionListener();
method public boolean clearApplicationUserData();
method public void clearWatchHeapLimit();
method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String);
@@ -4626,7 +4626,7 @@
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
- method @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
+ method @FlaggedApi("android.app.app_start_info") @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
method public int getLargeMemoryClass();
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
@@ -4653,7 +4653,7 @@
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
method @Deprecated public void restartPackage(String);
- method public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
+ method @FlaggedApi("android.app.app_start_info") public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
method public void setProcessStateSummary(@Nullable byte[]);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
@@ -5234,7 +5234,7 @@
field public static final int REASON_USER_STOPPED = 11; // 0xb
}
- public final class ApplicationStartInfo implements android.os.Parcelable {
+ @FlaggedApi("android.app.app_start_info") public final class ApplicationStartInfo implements android.os.Parcelable {
method public int describeContents();
method public int getDefiningUid();
method @Nullable public android.content.Intent getIntent();
@@ -39009,6 +39009,7 @@
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
method public int getMaxUsageCount();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -39016,6 +39017,7 @@
method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
method public boolean isUnlockedDeviceRequired();
@@ -39047,6 +39049,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39151,12 +39154,14 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method public int getMaxUsageCount();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isUnlockedDeviceRequired();
method public boolean isUserAuthenticationRequired();
@@ -39178,6 +39183,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9b8d2b4..052d614 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -556,24 +556,24 @@
package android.se.omapi {
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeFrameworkInitializer {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeFrameworkInitializer {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeServiceManager {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeServiceManager {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public SeServiceManager.ServiceNotFoundException(@NonNull String);
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public SeServiceManager.ServiceNotFoundException(@NonNull String);
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final class SeServiceManager.ServiceRegisterer {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder get();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void register(@NonNull android.os.IBinder);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder tryGet();
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public static final class SeServiceManager.ServiceRegisterer {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder get();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void register(@NonNull android.os.IBinder);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder tryGet();
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c3c3882..1d88e00 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -540,7 +540,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
- method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
+ method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
@@ -3193,7 +3193,7 @@
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull public android.content.Context createContext();
@@ -3214,9 +3214,9 @@
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
@@ -3232,7 +3232,7 @@
method public int getDefaultActivityPolicy();
method public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
- method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
method public int getLockState();
method @Nullable public String getName();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -3247,7 +3247,7 @@
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
- field @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+ field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3264,7 +3264,7 @@
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
- method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
@@ -3805,8 +3805,8 @@
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
- method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3817,8 +3817,8 @@
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
@@ -3879,7 +3879,7 @@
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
+ field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
}
public abstract class PackageManager {
@@ -3929,7 +3929,7 @@
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
- method @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
@@ -3980,7 +3980,7 @@
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 256; // 0x100
field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
- field @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
+ field @FlaggedApi("android.content.pm.quarantined_enabled") public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff
field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3
field public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; // 0xffffffee
@@ -9632,67 +9632,67 @@
package android.nfc.cardemulation {
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class AidGroup implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategory();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategoryForAid(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public String getOffHostSecureElement();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getPrefixAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSettingsActivityName();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getSubsetAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getNfcid2();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSystemCode();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getT3tPmm();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicNfcid2(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicSystemCode(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
}
}
@@ -9843,7 +9843,6 @@
field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
- field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
@@ -10706,13 +10705,13 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
- method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
- method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -16778,13 +16777,13 @@
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
field public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
field public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
field public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
field public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
field public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4f45691..3bf2cca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -24,6 +24,7 @@
import android.Manifest;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -3982,6 +3983,7 @@
* the order from most recent to least recent.
*/
@NonNull
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public List<ApplicationStartInfo> getHistoricalProcessStartReasons(
@IntRange(from = 0) int maxNum) {
try {
@@ -4012,6 +4014,7 @@
*/
@NonNull
@SystemApi
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
@RequiresPermission(Manifest.permission.DUMP)
public List<ApplicationStartInfo> getExternalHistoricalProcessStartReasons(
@NonNull String packageName, @IntRange(from = 0) int maxNum) {
@@ -4044,6 +4047,7 @@
*
* @throws IllegalArgumentException if executor or listener are null.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void setApplicationStartInfoCompletionListener(@NonNull final Executor executor,
@NonNull final Consumer<ApplicationStartInfo> listener) {
Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -4065,6 +4069,7 @@
/**
* Removes the callback set by {@link #setApplicationStartInfoCompletionListener} if there is one.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void clearApplicationStartInfoCompletionListener() {
try {
getService().clearApplicationStartInfoCompleteListener(mContext.getUserId());
@@ -4089,6 +4094,7 @@
* Will thow {@link java.lang.IllegalArgumentException} if not in range.
* @param timestampNs Clock monotonic time in nanoseconds of event to be recorded.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void addStartInfoTimestamp(@IntRange(
from = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START,
to = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int key,
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index f5fb6ed..a6a57cd 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@
/**
* Provide information related to a processes startup.
*/
+@FlaggedApi(Flags.FLAG_APP_START_INFO)
public final class ApplicationStartInfo implements Parcelable {
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7f38b27..ec5effd 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -98,6 +98,7 @@
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
+ ParceledListSlice getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid);
void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
void unlockNotificationChannel(String pkg, int uid, String channelId);
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
new file mode 100644
index 0000000..2076e85
--- /dev/null
+++ b/core/java/android/app/activity_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "system_performance"
+ name: "app_start_info"
+ description: "Control collecting of ApplicationStartInfo records and APIs."
+ bug: "247814855"
+}
\ No newline at end of file
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ed0f872..15bd1dc 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -22,8 +22,10 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -2452,7 +2454,7 @@
+ node.getTextStyle());
Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor())
+ ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
- Log.i(TAG, prefix + " Input type: " + node.getInputType());
+ Log.i(TAG, prefix + " Input type: " + getInputTypeString(node.getInputType()));
Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry());
}
String webDomain = node.getWebDomain();
@@ -2664,4 +2666,33 @@
return new AssistStructure[size];
}
};
+
+ private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>();
+ static {
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter");
+ }
+
+ private static String getInputTypeString(int inputType) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(inputType);
+ sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')');
+ for (int variation : INPUT_TYPE_VARIATIONS.keySet()) {
+ if ((variation & inputType) == variation) {
+ sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation));
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4700720..4791a83 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -698,4 +698,34 @@
return "AmbientLightSensorData(" + sensorName + ", " + sensorType + ")";
}
}
+
+ /**
+ * Associate a internal display to a {@link DisplayOffloader}.
+ *
+ * @param displayId the id of the internal display.
+ * @param displayOffloader the {@link DisplayOffloader} that controls offloading ops of internal
+ * display whose id is displayId.
+ * @return a {@link DisplayOffloadSession} associated with given displayId and displayOffloader.
+ */
+ public abstract DisplayOffloadSession registerDisplayOffloader(
+ int displayId, DisplayOffloader displayOffloader);
+
+ /** The callbacks that controls the entry & exit of display offloading. */
+ public interface DisplayOffloader {
+ boolean startOffload();
+
+ void stopOffload();
+ }
+
+ /** A session token that associates a internal display with a {@link DisplayOffloader}. */
+ public interface DisplayOffloadSession {
+ /** Provide the display state to use in place of state DOZE. */
+ void setDozeStateOverride(int displayState);
+ /** Returns the associated DisplayOffloader. */
+ DisplayOffloader getDisplayOffloader();
+ /** Returns whether displayoffload supports the given display state. */
+ static boolean isSupportedOffloadState(int displayState) {
+ return Display.isSuspendedState(displayState);
+ }
+ }
}
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
new file mode 100644
index 0000000..dbc1a4b
--- /dev/null
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.hardware.radio"
+
+flag {
+ name: "hd_radio_improved"
+ namespace: "car_framework"
+ description: "Feature flag for improved HD radio support with less vendor extensions"
+ bug: "280300929"
+}
diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md
index 04c658c..20f3d74 100644
--- a/core/java/android/net/network-policy-restrictions.md
+++ b/core/java/android/net/network-policy-restrictions.md
@@ -29,8 +29,8 @@
| **DS** | *AL* | ok | blk | ok | ok |
| **ON** | *!AL* | blk | blk | blk | blk |
| | *DL* | blk | blk | blk | blk |
-| **DS** | *AL* | blk | blk | ok | ok |
-| **OFF** | *!AL* | blk | blk | ok | ok |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **OFF** | *!AL* | ok | blk | ok | ok |
| | *DL* | blk | blk | blk | blk |
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index ada5532..f817fb8 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -32,7 +32,9 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -613,15 +615,35 @@
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
+
+ /* This list is to hold strong reference to the death recipients that are waiting for the death
+ * of binder that this proxy references. Previously, the death recipients were strongy
+ * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a
+ * strong reference from the death recipient to the proxy object. The JNI reference is now weak.
+ * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */
+ private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>());
+
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
+ public void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException {
+ linkToDeathNative(recipient, flags);
+ mDeathRecipients.add(recipient);
+ }
+
/**
* See {@link IBinder#unlinkToDeath}
*/
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ mDeathRecipients.remove(recipient);
+ return unlinkToDeathNative(recipient, flags);
+ }
+
+ private native void linkToDeathNative(DeathRecipient recipient, int flags)
+ throws RemoteException;
+
+ private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
* Perform a dump on the remote object
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index f10467f..47ad72f 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -124,6 +124,8 @@
/**
* Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+ *
+ * @hide
*/
public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 2dbb5da..dae3202 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -76,28 +76,38 @@
* Enables fs-verity to the owned file under the calling app's private directory. It always uses
* the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
*
- * The operation can only succeed when the file is not opened as writable by any process.
+ * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be
+ * writable by the app and not already have fs-verity enabled, and the file must not currently
+ * be open for writing by any process. To check whether the device supports fs-verity, use
+ * {@link #isApkVeritySupported()}.
*
- * It takes O(file size) time to build the underlying data structure for continuous
+ * <p>It takes O(file size) time to build the underlying data structure for continuous
* verification. The operation is atomic, i.e. it's either enabled or not, even in case of
* power failure during or after the call.
*
- * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+ * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to
* perform a signature check by itself before using the file. The signature is often delivered
* as a separate file and stored next to the targeting file in the filesystem. The public key of
* the signer (normally the same app developer) can be put in the APK, and the app can use the
* public key to verify the signature to the file's actual fs-verity digest (from {@link
- * #getFsVerityDigest}) before using the file. The exact format is not prescribed by the
+ * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the
* framework. App developers may choose to use common practices like JCA for the signing and
* verification, or their own preferred approach.
*
- * @param file The file to enable fs-verity. It should be an absolute path.
+ * @param file The file to enable fs-verity. It must represent an absolute path.
+ * @throws IllegalArgumentException If the provided file is not an absolute path.
+ * @throws IOException If the operation failed.
*
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
public void setupFsVerity(@NonNull File file) throws IOException {
if (!file.isAbsolute()) {
+ // fs-verity is to be enabled by installd, which enforces the validation to the
+ // (untrusted) file path passed from here. To make this less error prone, installd
+ // accepts only absolute path. When a relative path is provided, we fail with an
+ // explicit exception to help developers understand the requirement to use an absolute
+ // path.
throw new IllegalArgumentException("Expect an absolute path");
}
IFsveritySetupAuthToken authToken;
@@ -121,11 +131,12 @@
}
/**
- * Returns the fs-verity digest for the owned file under the calling app's
- * private directory, or null when the file does not have fs-verity enabled.
+ * Returns the fs-verity digest for the owned file under the calling app's private directory, or
+ * null when the file does not have fs-verity enabled (including when fs-verity is not supported
+ * on older devices).
*
* @param file The file to measure the fs-verity digest.
- * @return The fs-verity digeset in byte[], null if none.
+ * @return The fs-verity digest in byte[], null if none.
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94d8516..6a82f6d 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -959,8 +959,8 @@
mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
}
- notifyStateChangedLocked();
}
+ notifyStateChanged(availability);
}
/**
@@ -1370,8 +1370,8 @@
mAvailability = STATE_INVALID;
mIsAvailabilityOverriddenByTestApi = false;
- notifyStateChangedLocked();
}
+ notifyStateChanged(STATE_INVALID);
super.destroy();
}
@@ -1401,6 +1401,8 @@
*/
// TODO(b/281608561): remove the enrollment flow from AlwaysOnHotwordDetector
void onSoundModelsChanged() {
+ boolean notifyError = false;
+
synchronized (mLock) {
if (mAvailability == STATE_INVALID
|| mAvailability == STATE_HARDWARE_UNAVAILABLE
@@ -1441,6 +1443,9 @@
// calling stopRecognition where there is no started session.
Log.w(TAG, "Failed to stop recognition after enrollment update: code="
+ result);
+
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabilityTask().execute();
} catch (Exception e) {
Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
@@ -1449,14 +1454,14 @@
+ Log.getStackTraceString(e),
FailureSuggestedAction.RECREATE_DETECTOR));
} else {
- updateAndNotifyStateChangedLocked(STATE_ERROR);
+ notifyError = true;
}
- return;
}
}
+ }
- // Execute a refresh availability task - which should then notify of a change.
- new RefreshAvailabilityTask().execute();
+ if (notifyError) {
+ updateAndNotifyStateChanged(STATE_ERROR);
}
}
@@ -1572,10 +1577,11 @@
}
}
- @GuardedBy("mLock")
- private void updateAndNotifyStateChangedLocked(int availability) {
- updateAvailabilityLocked(availability);
- notifyStateChangedLocked();
+ private void updateAndNotifyStateChanged(int availability) {
+ synchronized (mLock) {
+ updateAvailabilityLocked(availability);
+ }
+ notifyStateChanged(availability);
}
@GuardedBy("mLock")
@@ -1589,17 +1595,17 @@
}
}
- @GuardedBy("mLock")
- private void notifyStateChangedLocked() {
+ private void notifyStateChanged(int newAvailability) {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
- message.arg1 = mAvailability;
+ message.arg1 = newAvailability;
message.sendToTarget();
}
- @GuardedBy("mLock")
private void sendUnknownFailure(String failureMessage) {
- // update but do not call onAvailabilityChanged callback for STATE_ERROR
- updateAvailabilityLocked(STATE_ERROR);
+ synchronized (mLock) {
+ // update but do not call onAvailabilityChanged callback for STATE_ERROR
+ updateAvailabilityLocked(STATE_ERROR);
+ }
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
}
@@ -1802,19 +1808,17 @@
availability = STATE_KEYPHRASE_UNENROLLED;
}
}
- updateAndNotifyStateChangedLocked(availability);
}
+ updateAndNotifyStateChanged(availability);
} catch (Exception e) {
// Any exception here not caught will crash the process because AsyncTask does not
// bubble up the exceptions to the client app, so we must propagate it to the app.
Slog.w(TAG, "Failed to refresh availability", e);
- synchronized (mLock) {
- if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
- sendUnknownFailure(
- "Failed to refresh availability: " + Log.getStackTraceString(e));
- } else {
- updateAndNotifyStateChangedLocked(STATE_ERROR);
- }
+ if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
+ sendUnknownFailure(
+ "Failed to refresh availability: " + Log.getStackTraceString(e));
+ } else {
+ updateAndNotifyStateChanged(STATE_ERROR);
}
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
new file mode 100644
index 0000000..46fa501
--- /dev/null
+++ b/core/java/android/text/ClientFlags.java
@@ -0,0 +1,58 @@
+/*
+ * 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.text;
+
+import com.android.text.flags.Flags;
+
+/**
+ * An aconfig feature flags that can be accessible from application process without
+ * ContentProvider IPCs.
+ *
+ * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
+ *
+ * @hide
+ */
+public class ClientFlags {
+
+ /**
+ * @see Flags#deprecateFontsXml()
+ */
+ public static boolean deprecateFontsXml() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_DEPRECATE_FONTS_XML);
+ }
+
+ /**
+ * @see Flags#noBreakNoHyphenationSpan()
+ */
+ public static boolean noBreakNoHyphenationSpan() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
+ }
+
+ /**
+ * @see Flags#phraseStrictFallback()
+ */
+ public static boolean phraseStrictFallback() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
+ }
+
+ /**
+ * @see Flags#useBoundsForWidth()
+ */
+ public static boolean useBoundsForWidth() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
+ }
+}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 4be6a8d..536e3cc 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -16,6 +16,11 @@
package android.text;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+
+import com.android.text.flags.Flags;
+
/**
* Flags in the "text" namespace.
*
@@ -46,4 +51,28 @@
*/
public static final boolean ENABLE_NEW_CONTEXT_MENU_DEFAULT = true;
+ /**
+ * List of text flags to be transferred to the application process.
+ */
+ public static final String[] TEXT_ACONFIGS_FLAGS = {
+ Flags.FLAG_DEPRECATE_FONTS_XML,
+ Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
+ Flags.FLAG_PHRASE_STRICT_FALLBACK,
+ Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
+ };
+
+ /**
+ * Get a key for the feature flag.
+ */
+ public static String getKeyForFlag(@NonNull String flag) {
+ return "text__" + flag;
+ }
+
+ /**
+ * Return true if the feature flag is enabled.
+ */
+ public static boolean isFeatureEnabled(@NonNull String flag) {
+ return AppGlobals.getIntCoreSetting(
+ getKeyForFlag(flag), 0 /* aconfig is false by default */) != 0;
+ }
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index a46136a..31d759e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -264,6 +264,16 @@
}
/**
+ * Called when a display hotplug event with connection error is received.
+ *
+ * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+ * timebase.
+ * @param connectionError the hotplug connection error code.
+ */
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ }
+
+ /**
* Called when a display mode changed event is received.
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
@@ -345,6 +355,11 @@
onHotplug(timestampNanos, physicalDisplayId, connected);
}
+ @SuppressWarnings("unused")
+ private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) {
+ onHotplugConnectionError(timestampNanos, connectionError);
+ }
+
// Called from native code.
@SuppressWarnings("unused")
private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0ba5d06..f421351 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3858,9 +3858,7 @@
mPendingTransitions.clear();
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
} else if (cancelAndRedraw) {
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
@@ -3874,8 +3872,8 @@
}
mPendingTransitions.clear();
}
- if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
+ if (!performDraw(mActiveSurfaceSyncGroup)) {
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
}
}
@@ -3890,6 +3888,7 @@
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
+ mHasPendingTransactions = false;
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
@@ -4688,7 +4687,8 @@
return false;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
+ final boolean fullRedrawNeeded =
+ mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions;
mFullRedrawNeeded = false;
mIsDrawing = true;
@@ -4718,8 +4718,15 @@
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
- if (mReportNextDraw) {
+ final Transaction pendingTransaction;
+ if (!usingAsyncReport && mHasPendingTransactions) {
+ pendingTransaction = new Transaction();
+ pendingTransaction.merge(mPendingTransaction);
+ } else {
+ pendingTransaction = null;
+ }
+ if (mReportNextDraw) {
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
@@ -4741,9 +4748,7 @@
if (mSurfaceHolder != null && mSurface.isValid()) {
usingAsyncReport = true;
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
- if (surfaceSyncGroup != null) {
- surfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
});
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4756,15 +4761,27 @@
}
}
- if (surfaceSyncGroup != null && !usingAsyncReport) {
- surfaceSyncGroup.markSyncReady();
+ if (!usingAsyncReport) {
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
}
+
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
return true;
}
+ private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup,
+ @Nullable Transaction pendingTransaction) {
+ if (surfaceSyncGroup != null) {
+ if (pendingTransaction != null) {
+ surfaceSyncGroup.addTransaction(pendingTransaction);
+ }
+ surfaceSyncGroup.markSyncReady();
+ } else if (pendingTransaction != null) {
+ pendingTransaction.apply();
+ }
+ }
/**
* Checks (and caches) if content capture is enabled for this context.
*/
@@ -4850,8 +4867,8 @@
}
}
- private boolean draw(boolean fullRedrawNeeded,
- @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) {
+ private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
+ boolean syncBuffer) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
@@ -4995,12 +5012,11 @@
mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
}
} else if (mHasPendingTransactions) {
- // Register a calback if there's no sync involved but there were calls to
+ // Register a callback if there's no sync involved but there were calls to
// applyTransactionOnDraw. If there is a sync involved, the sync callback will
// handle merging the pending transaction.
registerCallbackForPendingTransactions();
}
- mHasPendingTransactions = false;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
@@ -8977,13 +8993,7 @@
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- mActiveSurfaceSyncGroup = null;
- }
- if (mHasPendingTransactions) {
- mPendingTransaction.apply();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -11502,9 +11512,7 @@
Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
}
- Transaction t = new Transaction();
- t.merge(mPendingTransaction);
-
+ surfaceSyncGroup.addTransaction(mPendingTransaction);
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -11518,7 +11526,6 @@
+ frame + ".");
}
- mergeWithNextTransaction(t, frame);
// If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
// SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
// any blast sync or commit callback, and the code should directly call
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a0d0656..2c41330 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -103,6 +103,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
+import android.text.ClientFlags;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
@@ -1634,7 +1635,7 @@
}
if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = false; // TODO: Connect to the flag.
+ mUseBoundsForWidth = ClientFlags.useBoundsForWidth();
} else {
mUseBoundsForWidth = false;
}
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 9b10a7f..932608a3 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.view.WindowManager.transitTypeToString;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -88,6 +90,11 @@
this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
}
+ /** @hide */
+ String typeToString() {
+ return transitTypeToString(mType);
+ }
+
/** Requested change to a display. */
@DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
public static class DisplayChange implements Parcelable {
@@ -263,7 +270,7 @@
};
@DataClass.Generated(
- time = 1693425051905L,
+ time = 1695667226050L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -298,11 +305,11 @@
* @param type
* The type of the transition being requested.
* @param triggerTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
* @param pipTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
- * finish) has caused this transition to occur.
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -431,7 +438,7 @@
// String fieldNameToString() { ... }
return "TransitionRequestInfo { " +
- "type = " + mType + ", " +
+ "type = " + typeToString() + ", " +
"triggerTask = " + mTriggerTask + ", " +
"pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
@@ -506,10 +513,10 @@
};
@DataClass.Generated(
- time = 1693425051928L,
+ time = 1695667226088L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0d1871d..663067c 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -203,14 +203,22 @@
}
return false;
}
-
+
+ /**
+ * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
+ * Intent.ACTION_PACKAGE_CHANGED} being received, this callback
+ * has extras passed in.
+ */
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ }
+
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
return false;
}
public void onHandleUserStop(Intent intent, int userHandle) {
}
-
+
public void onUidRemoved(int uid) {
}
@@ -238,21 +246,34 @@
}
/**
+ * Called when a package disappears with extras passed in.
+ */
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ }
+
+ /**
* Called when a package appears for any reason.
*/
public void onPackageAppeared(String packageName, int reason) {
}
+
+ /**
+ * Called when a package appears with extras passed in.
+ */
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ }
+
/**
* Called when an existing package is updated or its disabled state changes.
*/
public void onPackageModified(@NonNull String packageName) {
}
-
+
public boolean didSomePackagesChange() {
return mSomePackagesChanged;
}
-
+
public int isPackageAppearing(String packageName) {
if (mAppearingPackages != null) {
for (int i=mAppearingPackages.length-1; i>=0; i--) {
@@ -381,6 +402,7 @@
mChangeType = PACKAGE_PERMANENT_CHANGE;
onPackageAdded(pkg, uid);
}
+ onPackageAppearedWithExtras(pkg, intent.getExtras());
onPackageAppeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
@@ -403,6 +425,7 @@
onPackageRemovedAllUsers(pkg, uid);
}
}
+ onPackageDisappearedWithExtras(pkg, intent.getExtras());
onPackageDisappeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -417,6 +440,7 @@
if (onPackageChanged(pkg, uid, mModifiedComponents)) {
mSomePackagesChanged = true;
}
+ onPackageChangedWithExtras(pkg, intent.getExtras());
onPackageModified(pkg);
}
} else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index d503904..7a87c3a 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -16,9 +16,11 @@
package com.android.internal.display;
+import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
@@ -54,8 +56,7 @@
private static final int MSG_RUN_UPDATE = 1;
// The tolerance within which we consider brightness values approximately equal to eachother.
- // This value is approximately 1/3 of the smallest possible brightness value.
- public static final float EPSILON = 0.001f;
+ public static final float EPSILON = 0.0001f;
private static int sBrightnessUpdateCount = 1;
@@ -284,6 +285,74 @@
}
/**
+ * Converts between the int brightness setting and the float brightness system. The int
+ * brightness setting is between 0-255 and matches the brightness slider - e.g. 128 is 50% on
+ * the slider. Accounts for special values such as OFF and invalid values. Accounts for
+ * brightness limits; the maximum value here represents the max value allowed on the slider.
+ */
+ @VisibleForTesting
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public float brightnessIntSettingToFloat(int brightnessInt) {
+ if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
+ return PowerManager.BRIGHTNESS_OFF_FLOAT;
+ } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ } else {
+ final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+ final float maxInt = PowerManager.BRIGHTNESS_ON;
+
+ // Normalize to the range [0, 1]
+ float userPerceptionBrightness = MathUtils.norm(minInt, maxInt, brightnessInt);
+
+ // Convert from user-perception to linear scale
+ float linearBrightness = BrightnessUtils.convertGammaToLinear(userPerceptionBrightness);
+
+ // Interpolate to the range [0, currentlyAllowedMax]
+ final Display display = mContext.getDisplay();
+ if (display == null) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+ final BrightnessInfo info = display.getBrightnessInfo();
+ return MathUtils.lerp(info.brightnessMinimum, info.brightnessMaximum, linearBrightness);
+ }
+ }
+
+ /**
+ * Translates specified value from the float brightness system to the setting int brightness
+ * system. The value returned is between 0-255 and matches the brightness slider - e.g. 128 is
+ * 50% on the slider. Accounts for special values such as OFF and invalid values. Accounts for
+ * brightness limits; the maximum value here represents the max value currently allowed on
+ * the slider.
+ */
+ @VisibleForTesting
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public int brightnessFloatToIntSetting(float brightnessFloat) {
+ if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
+ return PowerManager.BRIGHTNESS_OFF;
+ } else if (Float.isNaN(brightnessFloat)) {
+ return PowerManager.BRIGHTNESS_INVALID;
+ } else {
+ // Normalize to the range [0, 1]
+ final Display display = mContext.getDisplay();
+ if (display == null) {
+ return PowerManager.BRIGHTNESS_INVALID;
+ }
+ final BrightnessInfo info = display.getBrightnessInfo();
+ float linearBrightness =
+ MathUtils.norm(info.brightnessMinimum, info.brightnessMaximum, brightnessFloat);
+
+ // Convert from linear to user-perception scale
+ float userPerceptionBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
+
+ // Interpolate to the range [0, 255]
+ final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+ final float maxInt = PowerManager.BRIGHTNESS_ON;
+ float intBrightness = MathUtils.lerp(minInt, maxInt, userPerceptionBrightness);
+ return Math.round(intBrightness);
+ }
+ }
+
+ /**
* Encapsulates a brightness change event and contains logic for synchronizing the appropriate
* settings for the specified brightness change.
*/
@@ -421,14 +490,14 @@
if (mSourceType == TYPE_INT) {
return (int) mBrightness;
}
- return brightnessFloatToInt(mBrightness);
+ return brightnessFloatToIntSetting(mBrightness);
}
private float getBrightnessAsFloat() {
if (mSourceType == TYPE_FLOAT) {
return mBrightness;
}
- return brightnessIntToFloat((int) mBrightness);
+ return brightnessIntSettingToFloat((int) mBrightness);
}
private String toStringLabel(int type, float brightness) {
diff --git a/services/core/java/com/android/server/display/BrightnessUtils.java b/core/java/com/android/internal/display/BrightnessUtils.java
similarity index 96%
rename from services/core/java/com/android/server/display/BrightnessUtils.java
rename to core/java/com/android/internal/display/BrightnessUtils.java
index 84fa0cc..82b506b 100644
--- a/services/core/java/com/android/server/display/BrightnessUtils.java
+++ b/core/java/com/android/internal/display/BrightnessUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.display;
+package com.android.internal.display;
import android.util.MathUtils;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c6f5086..7eeac29 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -154,6 +154,7 @@
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
+ void setQsTiles(in String[] tiles);
void clickQsTile(in ComponentName tile);
void handleSystemKey(in KeyEvent key);
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index acb0e44..89f4659 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -49,6 +49,7 @@
private CachingIconView mIcon;
private CachingIconView mConversationIconBadgeBg;
private TextView mConversationText;
+ private boolean mSetDataAsyncEnabled = false;
public CallLayout(@NonNull Context context) {
super(context);
@@ -83,7 +84,8 @@
});
}
- private void updateCallLayout() {
+ @NonNull
+ private Icon getConversationIcon() {
CharSequence callerName = "";
String symbol = "";
Icon icon = null;
@@ -98,8 +100,7 @@
if (icon == null) {
icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
}
- // TODO(b/179178086): crop/clip the icon to a circle?
- mConversationIconView.setImageIcon(icon);
+ return icon;
}
@RemotableViewMethod
@@ -123,10 +124,38 @@
/**
* Set the notification extras so that this layout has access
*/
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setDataAsync")
public void setData(Bundle extras) {
- setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON, android.app.Person.class));
- updateCallLayout();
+ final Person person = getPerson(extras);
+ setUser(person);
+
+ final Icon icon = getConversationIcon();
+ mConversationIconView.setImageIcon(icon);
+ }
+
+
+ public void setSetDataAsyncEnabled(boolean setDataAsyncEnabled) {
+ mSetDataAsyncEnabled = setDataAsyncEnabled;
+ }
+
+ /**
+ * Async implementation for setData
+ */
+ public Runnable setDataAsync(Bundle extras) {
+ if (!mSetDataAsyncEnabled) {
+ return () -> setData(extras);
+ }
+
+ final Person person = getPerson(extras);
+ setUser(person);
+
+ final Icon conversationIcon = getConversationIcon();
+ return mConversationIconView.setImageIconAsync(conversationIcon);
+ }
+
+ @Nullable
+ private Person getPerson(Bundle extras) {
+ return extras.getParcelable(Notification.EXTRA_CALL_PERSON, Person.class);
}
private void setUser(Person user) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index ad196c0..9ed4155 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -15,7 +15,19 @@
],
}
-cc_library_shared {
+soong_config_module_type {
+ name: "cc_library_shared_for_libandroid_runtime",
+ module_type: "cc_library_shared",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_binder_death_recipient_weak_from_jni",
+ ],
+ properties: [
+ "cflags",
+ ],
+}
+
+cc_library_shared_for_libandroid_runtime {
name: "libandroid_runtime",
host_supported: true,
cflags: [
@@ -46,6 +58,12 @@
},
},
+ soong_config_variables: {
+ release_binder_death_recipient_weak_from_jni: {
+ cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
+ },
+ },
+
cpp_std: "gnu++20",
srcs: [
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 041f9c7..bfd80a9e 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -17,19 +17,8 @@
#define LOG_TAG "JavaBinder"
//#define LOG_NDEBUG 0
-#include "android_os_Parcel.h"
#include "android_util_Binder.h"
-#include <atomic>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <mutex>
-#include <stdio.h>
-#include <string>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -40,7 +29,16 @@
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
#include <cutils/atomic.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
@@ -48,10 +46,11 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <atomic>
+#include <mutex>
+#include <string>
+#include "android_os_Parcel.h"
#include "core_jni_helpers.h"
//#undef ALOGV
@@ -553,14 +552,48 @@
};
// ----------------------------------------------------------------------------
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+#if __BIONIC__
+#include <android/api-level.h>
+static bool target_sdk_is_at_least_vic() {
+ return android_get_application_target_sdk_version() >= __ANDROID_API_V__;
+}
+#else
+static constexpr bool target_sdk_is_at_least_vic() {
+ // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat
+ // requirement there.
+ return true;
+}
+#endif // __BIONIC__
+#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:
JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
- : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
- mObjectWeak(NULL), mList(list)
- {
+ : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
+ // to the death recipient objects. This is to prevent the memory leak which can happen when
+ // the death recipient object internally has a strong reference to the proxy object. Under
+ // the old behavior, you were unable to kill the binder service by dropping all references
+ // to the proxy object - because it is still strong referenced from JNI (here). The only way
+ // to cut the strong reference was to call unlinkDeath(), but it was easy to forget.
+ //
+ // Now, the strong reference to the death recipient is held in the Java-side proxy object.
+ // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An
+ // implication of this is that you may not receive binderDied() if you drop all references
+ // to the proxy object before the service dies. This should be okay for most cases because
+ // you normally are not interested in the death of a binder service which you don't have any
+ // reference to. If however you want to get binderDied() regardless of the proxy object's
+ // lifecycle, keep a strong reference to the death recipient object by yourself.
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+ if (target_sdk_is_at_least_vic()) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else
+#endif
+ {
+ mObject = env->NewGlobalRef(object);
+ }
// These objects manage their own lifetimes so are responsible for final bookkeeping.
// The list holds a strong reference to this object.
LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
@@ -573,26 +606,49 @@
void binderDied(const wp<IBinder>& who)
{
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
- if (mObject != NULL) {
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject,
- jBinderProxy.get());
- if (env->ExceptionCheck()) {
- jthrowable excep = env->ExceptionOccurred();
- binder_report_exception(env, excep,
- "*** Uncaught exception returned from death notification!");
- }
+ if (mObject == NULL && mObjectWeak == NULL) {
+ return;
+ }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- // Serialize with our containing DeathRecipientList so that we can't
- // delete the global ref on mObject while the list is being iterated.
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the death notice.
+ ScopedLocalRef<jobject> jRecipient(env,
+ env->NewLocalRef(mObject != NULL ? mObject
+ : mObjectWeak));
+ if (jRecipient.get() == NULL) {
+ ALOGW("Binder died, but death recipient is already garbage collected. If your target "
+ "sdk level is at or above 35, this can happen when you dropped all references to "
+ "the binder service before it died. If you want to get a death notice for a "
+ "binder service which you have no reference to, keep a strong reference to the "
+ "death recipient by yourself.");
+ return;
+ }
+
+ if (mFired) {
+ ALOGW("Received multiple death notices for the same binder object. Binder driver bug?");
+ return;
+ }
+ mFired = true;
+
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice,
+ jRecipient.get(), jBinderProxy.get());
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from death notification!");
+ }
+
+ // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to
+ // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync
+ // with our containing DeathRecipientList so that we can't delete the global ref on mObject
+ // while the list is being iterated.
+ if (mObject != NULL) {
sp<DeathRecipientList> list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
- // Demote from strong ref to weak after binderDied() has been delivered,
- // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
mObjectWeak = env->NewWeakGlobalRef(mObject);
env->DeleteGlobalRef(mObject);
mObject = NULL;
@@ -659,9 +715,19 @@
private:
JavaVM* const mVM;
- jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied().
- jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied().
+
+ // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
+ // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
+ // a weak reference (mObjectWeak).
+ // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side DeathRecipient is made in
+ // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
wp<DeathRecipientList> mList;
+
+ // Whether binderDied was called or not.
+ bool mFired = false;
};
// ----------------------------------------------------------------------------
@@ -1435,17 +1501,19 @@
// ----------------------------------------------------------------------------
+// clang-format off
static const JNINativeMethod gBinderProxyMethods[] = {
/* name, signature, funcPtr */
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
- {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
- {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
+ {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
+// clang-format on
const char* const kBinderProxyPathName = "android/os/BinderProxy";
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 69fc515..41c65ae 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -38,6 +38,7 @@
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
+ jmethodID dispatchHotplugConnectionError;
jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
@@ -89,6 +90,7 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override;
void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
nsecs_t renderPeriod) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -230,6 +232,22 @@
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
+void NativeDisplayEventReceiver::dispatchHotplugConnectionError(nsecs_t timestamp,
+ int connectionError) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hotplug dispatchHotplugConnectionError handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError,
+ timestamp, connectionError);
+ ALOGV("receiver %p ~ Returned from hotplug dispatchHotplugConnectionError handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHotplugConnectionError");
+}
+
void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t modeId, nsecs_t renderPeriod) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -354,8 +372,12 @@
gDisplayEventReceiverClassInfo.dispatchVsync =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
- gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplug =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug",
+ "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
+ "dispatchHotplugConnectionError", "(JI)V");
gDisplayEventReceiverClassInfo.dispatchModeChanged =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
"(JJIJ)V");
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a1adfc3..c00a776f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4830,7 +4830,7 @@
<string translatable="false" name="config_deviceSpecificInputMethodManagerService"></string>
<!-- Component name of media projection permission dialog -->
- <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.mediaprojection.MediaProjectionPermissionActivity</string>
+ <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">28dp</dimen>
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index e082c25..c7eddabe 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -17,7 +17,6 @@
package com.android.internal.content;
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +27,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -36,6 +36,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -45,6 +46,7 @@
@RunWith(AndroidJUnit4.class)
public class PackageMonitorTest {
private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp";
+ private static final String FAKE_EXTRA_REASON = "android.intent.extra.fakereason";
private static final int FAKE_PACKAGE_UID = 123;
private static final int FAKE_USER_ID = 0;
private static final int WAIT_CALLBACK_CALLED_IN_MS = 300;
@@ -245,6 +247,7 @@
intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REASON, FAKE_EXTRA_REASON);
String [] packageList = new String[]{FAKE_PACKAGE_NAME};
intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList);
spyPackageMonitor.doHandlePackageEvent(intent);
@@ -253,6 +256,20 @@
verify(spyPackageMonitor, times(1))
.onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageChangedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REASON))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REASON));
+
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
}
@@ -272,6 +289,21 @@
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1))
.onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
@@ -295,6 +327,21 @@
.onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1))
.onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -316,6 +363,19 @@
verify(spyPackageMonitor, times(1))
.onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1))
.onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -336,6 +396,19 @@
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 3956241..96c257b 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -16,6 +16,7 @@
package android.security.keystore;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +35,10 @@
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
@@ -300,6 +304,7 @@
private final Date mKeyValidityForConsumptionEnd;
private final @KeyProperties.PurposeEnum int mPurposes;
private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -345,6 +350,7 @@
Date keyValidityForConsumptionEnd,
@KeyProperties.PurposeEnum int purposes,
@KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.DigestEnum Set<String> mgf1Digests,
@KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
@KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
@KeyProperties.BlockModeEnum String[] blockModes,
@@ -404,6 +410,9 @@
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
mPurposes = purposes;
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ // No need to copy the input parameter because the Builder class passes in an immutable
+ // collection.
+ mMgf1Digests = mgf1Digests != null ? mgf1Digests : Collections.emptySet();
mEncryptionPaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
@@ -566,7 +575,7 @@
/**
* Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the
- * key can be used or {@code null} if not specified.
+ * key can be used.
*
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*
@@ -594,6 +603,40 @@
}
/**
+ * Returns the set of digests that can be used by the MGF1 mask generation function
+ * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+ * scheme.
+ * If not explicitly specified during key generation, the default {@code SHA-1} digest is
+ * used and may be specified when using the key.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isMgf1DigestsSpecified()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+ if (mMgf1Digests.isEmpty()) {
+ throw new IllegalStateException("Mask generation function (MGF) not specified");
+ }
+ return new HashSet(mMgf1Digests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+ * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+ *
+ * @see #getMgf1Digests()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public boolean isMgf1DigestsSpecified() {
+ return !mMgf1Digests.isEmpty();
+ }
+
+ /**
* Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding},
* {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
* encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -913,6 +956,8 @@
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyProperties.DigestEnum String[] mDigests;
+ private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+ Collections.emptySet();
private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -983,6 +1028,9 @@
if (sourceSpec.isDigestsSpecified()) {
mDigests = sourceSpec.getDigests();
}
+ if (sourceSpec.isMgf1DigestsSpecified()) {
+ mMgf1Digests = sourceSpec.getMgf1Digests();
+ }
mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
mSignaturePaddings = sourceSpec.getSignaturePaddings();
mBlockModes = sourceSpec.getBlockModes();
@@ -1230,6 +1278,30 @@
}
/**
+ * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+ * used by the mask generation function MGF1 (which is used for certain operations with
+ * the key). Attempts to use the key with any other digest for the mask generation
+ * function will be rejected.
+ *
+ * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+ * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+ * function (MGF1) with a digest.
+ * The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation
+ * time if no digests have been explicitly provided.
+ * When using the key, the caller may not specify any digests that were not provided during
+ * key creation time. The caller may specify the default digest, {@code SHA-1}, if no
+ * digests were explicitly provided during key creation (but it is not necessary to do so).
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ mMgf1Digests = Set.of(mgf1Digests);
+ return this;
+ }
+
+ /**
* Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
* {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
* encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -1782,6 +1854,7 @@
mKeyValidityForConsumptionEnd,
mPurposes,
mDigests,
+ mMgf1Digests,
mEncryptionPaddings,
mSignaturePaddings,
mBlockModes,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 5ab21bc..c1e3bab 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -16,6 +16,7 @@
package android.security.keystore;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +31,10 @@
import java.security.KeyStore.ProtectionParameter;
import java.security.Signature;
import java.security.cert.Certificate;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -223,6 +227,7 @@
private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
@@ -247,6 +252,7 @@
@KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
@KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
@KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.DigestEnum Set<String> mgf1Digests,
@KeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
@@ -271,6 +277,7 @@
mSignaturePaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ mMgf1Digests = mgf1Digests;
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
@@ -381,6 +388,40 @@
}
/**
+ * Returns the set of digests that can be used by the MGF1 mask generation function
+ * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+ * scheme.
+ * If not explicitly specified during key generation, the default {@code SHA-1} digest is
+ * used and may be specified.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isMgf1DigestsSpecified()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+ if (mMgf1Digests.isEmpty()) {
+ throw new IllegalStateException("Mask generation function (MGF) not specified");
+ }
+ return new HashSet(mMgf1Digests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+ * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+ *
+ * @see #getMgf1Digests()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public boolean isMgf1DigestsSpecified() {
+ return !mMgf1Digests.isEmpty();
+ }
+
+ /**
* Gets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be used
* when encrypting/decrypting. Attempts to use the key with any other block modes will be
* rejected.
@@ -588,6 +629,8 @@
private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private @KeyProperties.DigestEnum String[] mDigests;
+ private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+ Collections.emptySet();
private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
@@ -739,6 +782,30 @@
}
/**
+ * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+ * used by the mask generation function MGF1 (which is used for certain operations with
+ * the key). Attempts to use the key with any other digest for the mask generation
+ * function will be rejected.
+ *
+ * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+ * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+ * function (MGF1) with a digest.
+ * The default digest for MGF1 is {@code SHA-1}, which will be specified during key import
+ * time if no digests have been explicitly provided.
+ * When using the key, the caller may not specify any digests that were not provided during
+ * key import time. The caller may specify the default digest, {@code SHA-1}, if no
+ * digests were explicitly provided during key import (but it is not necessary to do so).
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ mMgf1Digests = Set.of(mgf1Digests);
+ return this;
+ }
+
+ /**
* Sets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be
* used when encrypting/decrypting. Attempts to use the key with any other block modes will
* be rejected.
@@ -1141,6 +1208,7 @@
mEncryptionPaddings,
mSignaturePaddings,
mDigests,
+ mMgf1Digests,
mBlockModes,
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 9356eb8..ceba04e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -23,7 +23,11 @@
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
+import java.util.Set;
import javax.security.auth.x500.X500Principal;
@@ -91,6 +95,11 @@
} else {
out.writeStringArray(null);
}
+ if (mSpec.isMgf1DigestsSpecified()) {
+ out.writeStringList(List.copyOf(mSpec.getMgf1Digests()));
+ } else {
+ out.writeStringList(null);
+ }
out.writeStringArray(mSpec.getEncryptionPaddings());
out.writeStringArray(mSpec.getSignaturePaddings());
out.writeStringArray(mSpec.getBlockModes());
@@ -153,6 +162,7 @@
final Date keyValidityForOriginationEnd = readDateOrNull(in);
final Date keyValidityForConsumptionEnd = readDateOrNull(in);
final String[] digests = in.createStringArray();
+ final ArrayList<String> mgf1Digests = in.createStringArrayList();
final String[] encryptionPaddings = in.createStringArray();
final String[] signaturePaddings = in.createStringArray();
final String[] blockModes = in.createStringArray();
@@ -191,6 +201,7 @@
keyValidityForConsumptionEnd,
purposes,
digests,
+ mgf1Digests != null ? Set.copyOf(mgf1Digests) : Collections.emptySet(),
encryptionPaddings,
signaturePaddings,
blockModes,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 9ac0f6d..101a10e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -24,6 +24,7 @@
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
import android.security.keystore.KeyStoreCryptoOperation;
import android.system.keystore2.Authorization;
@@ -71,7 +72,7 @@
*/
abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
private static final String TAG = "AndroidKeyStoreCipherSpiBase";
- public static final String DEFAULT_MGF1_DIGEST = "SHA-1";
+ public static final String DEFAULT_MGF1_DIGEST = KeyProperties.DIGEST_SHA1;
// Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
// doFinal finishes.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 1398da3..ed4b485 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -188,6 +188,7 @@
private int[] mKeymasterEncryptionPaddings;
private int[] mKeymasterSignaturePaddings;
private int[] mKeymasterDigests;
+ private int[] mKeymasterMgf1Digests;
private Long mRSAPublicExponent;
@@ -323,6 +324,21 @@
} else {
mKeymasterDigests = EmptyArray.INT;
}
+ if (spec.isMgf1DigestsSpecified()) {
+ // User-specified digests: Add all of them and do _not_ add the SHA-1
+ // digest by default (stick to what the user provided).
+ Set<String> mgfDigests = spec.getMgf1Digests();
+ mKeymasterMgf1Digests = new int[mgfDigests.size()];
+ int offset = 0;
+ for (String digest : mgfDigests) {
+ mKeymasterMgf1Digests[offset] = KeyProperties.Digest.toKeymaster(digest);
+ offset++;
+ }
+ } else {
+ // No user-specified digests: Add the SHA-1 default.
+ mKeymasterMgf1Digests = new int[]{
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)};
+ }
// Check that user authentication related parameters are acceptable. This method
// will throw an IllegalStateException if there are issues (e.g., secure lock screen
@@ -544,6 +560,7 @@
mKeymasterEncryptionPaddings = null;
mKeymasterSignaturePaddings = null;
mKeymasterDigests = null;
+ mKeymasterMgf1Digests = null;
mKeySizeBits = 0;
mSpec = null;
mRSAPublicExponent = null;
@@ -831,24 +848,11 @@
KeymasterDefs.KM_TAG_PADDING, padding
));
if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
- final boolean[] hasDefaultMgf1DigestBeenAdded = {false};
- ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+ ArrayUtils.forEach(mKeymasterMgf1Digests, (mgf1Digest) -> {
params.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest
));
- hasDefaultMgf1DigestBeenAdded[0] |=
- digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST));
});
- /* Because of default MGF1 digest is SHA-1. It has to be added in Key
- * characteristics. Otherwise, crypto operations will fail with Incompatible
- * MGF1 digest.
- */
- if (!hasDefaultMgf1DigestBeenAdded[0]) {
- params.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
- ));
- }
}
});
ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 273dff1..ddbd93e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -526,25 +526,22 @@
padding
));
if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
- if (spec.isDigestsSpecified()) {
- boolean hasDefaultMgf1DigestBeenAdded = false;
- for (String digest : spec.getDigests()) {
+ if (spec.isMgf1DigestsSpecified()) {
+ for (String mgf1Digest : spec.getMgf1Digests()) {
importArgs.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(digest)
+ KeyProperties.Digest.toKeymaster(mgf1Digest)
));
- hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST);
}
+ } else {
/* Because of default MGF1 digest is SHA-1. It has to be added in Key
* characteristics. Otherwise, crypto operations will fail with Incompatible
* MGF1 digest.
*/
- if (!hasDefaultMgf1DigestBeenAdded) {
- importArgs.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
- ));
- }
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+ ));
}
}
}
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 2ae61ab..d4e2dbc 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -17,6 +17,7 @@
package android.security;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -101,6 +102,7 @@
assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
@@ -189,4 +191,19 @@
ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
assertEquals(parcelSpec.getName(), ecSpec.getName());
}
+
+ @Test
+ public void testParcelingMgf1Digests() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+
+ ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+ new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests)
+ .build());
+ Parcel parcel = parcelForReading(spec);
+ KeyGenParameterSpec fromParcel =
+ ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+ assertArrayEquals(fromParcel.getMgf1Digests().toArray(), mgf1Digests);
+ }
}
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index ddbb1d8..da5e8bf 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -16,9 +16,12 @@
package android.security.keystore;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
import android.security.ParcelableKeyGenParameterSpecTest;
@@ -61,4 +64,54 @@
assertEquals(copiedSpec.getAttestationChallenge(), null);
}
+
+ @Test
+ public void testMgf1DigestsNotSpecifiedByDefault() {
+ KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
+ assertThrows(IllegalStateException.class, () -> {
+ spec.getMgf1Digests();
+ });
+ }
+
+ @Test
+ public void testMgf1DigestsCanBeSpecified() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests)
+ .build();
+ assertThat(spec.isMgf1DigestsSpecified(), is(true));
+ assertThat(spec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+
+ KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build();
+ assertThat(copiedSpec.isMgf1DigestsSpecified(), is(true));
+ assertThat(copiedSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+ }
+
+ @Test
+ public void testMgf1DigestsAreNotModified() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests);
+
+ KeyGenParameterSpec firstSpec = builder.build();
+ assertArrayEquals(mgf1Digests, firstSpec.getMgf1Digests().toArray());
+
+ String[] otherDigests = new String[] {KeyProperties.DIGEST_SHA224};
+ KeyGenParameterSpec secondSpec = builder.setMgf1Digests(otherDigests).build();
+ assertThat(secondSpec.getMgf1Digests(), containsInAnyOrder(otherDigests));
+
+ // Now check that the first spec created hasn't changed.
+ assertThat(firstSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+ }
+
+ @Test
+ public void testEmptyMgf1DigestsCanBeSet() {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(new String[] {}).build();
+
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index 5eeb3b6..b141beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -18,12 +18,16 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -36,14 +40,29 @@
*/
public class UserAspectRatioSettingsLayout extends LinearLayout {
+ private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+
+ private static final Interpolator PATH_INTERPOLATOR =
+ new PathInterpolator(0.2f, 0f, 0f, 1f);
+
private static final float ALPHA_FULL_TRANSPARENT = 0f;
private static final float ALPHA_FULL_OPAQUE = 1f;
- private static final long VISIBILITY_ANIMATION_DURATION_MS = 50;
+ private static final float SCALE_START = 0.8f;
+
+ private static final float SCALE_END = 1f;
+
+ private static final long FADE_ANIMATION_DURATION_MS = 167;
+
+ private static final long SCALE_ANIMATION_DURATION_MS = 300;
private static final String ALPHA_PROPERTY_NAME = "alpha";
+ private static final String SCALE_X_PROPERTY_NAME = "scaleX";
+
+ private static final String SCALE_Y_PROPERTY_NAME = "scaleY";
+
private UserAspectRatioSettingsWindowManager mWindowManager;
public UserAspectRatioSettingsLayout(Context context) {
@@ -88,7 +107,7 @@
if (show) {
showItem(view);
} else {
- view.setVisibility(visibility);
+ hideItem(view);
}
}
@@ -121,16 +140,40 @@
}
private void showItem(@NonNull View view) {
- view.setVisibility(View.VISIBLE);
+ final AnimatorSet animatorSet = new AnimatorSet();
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
- fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
- fadeIn.addListener(new AnimatorListenerAdapter() {
+ fadeIn.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeIn.setInterpolator(LINEAR_INTERPOLATOR);
+ final ObjectAnimator scaleY =
+ ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END);
+ final ObjectAnimator scaleX =
+ ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END);
+ scaleX.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleX.setInterpolator(PATH_INTERPOLATOR);
+ scaleY.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleY.setInterpolator(PATH_INTERPOLATOR);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationStart(Animator animation) {
view.setVisibility(View.VISIBLE);
}
});
- fadeIn.start();
+ animatorSet.playTogether(fadeIn, scaleY, scaleX);
+ animatorSet.start();
+ }
+
+ private void hideItem(@NonNull View view) {
+ final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
+ ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT);
+ fadeOut.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeOut.setInterpolator(LINEAR_INTERPOLATOR);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ }
+ });
+ fadeOut.start();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f8dd208..09ba4f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -37,6 +37,7 @@
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.RemoteTransition
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
@@ -65,7 +66,9 @@
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.ShellSharedConstants
+import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -139,20 +142,22 @@
}
/** Show all tasks, that are part of the desktop, on top of launcher */
- fun showDesktopApps(displayId: Int) {
+ fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
val wct = WindowContainerTransaction()
- // TODO(b/278084491): pass in display id
bringDesktopAppsToFront(displayId, wct)
- // Execute transaction if there are pending operations
- if (!wct.isEmpty) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/268662477): add animation for the transition
- transitions.startTransition(TRANSIT_NONE, wct, null /* handler */)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/255649902): ensure remote transition is supplied once state is introduced
+ val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
+ val handler = remoteTransition?.let {
+ OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
}
+ transitions.startTransition(transitionType, wct, handler).also { t ->
+ handler?.setTransition(t)
+ }
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
}
}
@@ -1093,11 +1098,11 @@
controller = null
}
- override fun showDesktopApps(displayId: Int) {
+ override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
"showDesktopApps"
- ) { c -> c.showDesktopApps(displayId) }
+ ) { c -> c.showDesktopApps(displayId, remoteTransition) }
}
override fun stashDesktopApps(displayId: Int) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 47edfd4..6bdaf1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode;
import android.app.ActivityManager.RunningTaskInfo;
+import android.window.RemoteTransition;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
/**
@@ -25,7 +26,7 @@
interface IDesktopMode {
/** Show apps on the desktop on the given display */
- void showDesktopApps(int displayId);
+ void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
/** Stash apps on the desktop to allow launching another app from home screen */
void stashDesktopApps(int displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0d77a2e..ef8393c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -29,12 +29,16 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.concurrent.Executor;
/**
@@ -74,6 +78,7 @@
private final TaskViewTaskController mTaskViewTaskController;
private Region mObscuredTouchRegion;
private Insets mCaptionInsets;
+ private Handler mHandler;
public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
@@ -81,6 +86,7 @@
// TODO(b/266736992): Think about a better way to set the TaskViewBase on the
// TaskViewTaskController and vice-versa
mTaskViewTaskController.setTaskViewBase(this);
+ mHandler = Handler.getMain();
getHolder().addCallback(this);
}
@@ -117,14 +123,16 @@
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
onLocationChanged();
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
}
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
}
}
@@ -143,7 +151,7 @@
@Override
public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
- setResizeBackgroundColor(t, bgColor);
+ runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
}
/**
@@ -272,12 +280,14 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mHandler = getHandler();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ mHandler = Handler.getMain();
}
/** Returns the task info for the task in the TaskView. */
@@ -285,4 +295,24 @@
public ActivityManager.RunningTaskInfo getTaskInfo() {
return mTaskViewTaskController.getTaskInfo();
}
+
+ /**
+ * Sets the handler, only for testing.
+ */
+ @VisibleForTesting
+ void setHandler(Handler viewHandler) {
+ mHandler = viewHandler;
+ }
+
+ /**
+ * Ensures that the given runnable runs on the view's thread.
+ */
+ private void runOnViewThread(Runnable r) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ r.run();
+ } else {
+ // If this call is not from the same thread as the view, then post it
+ mHandler.post(r);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
new file mode 100644
index 0000000..0df42b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
@@ -0,0 +1,59 @@
+/*
+ * 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.wm.shell;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
+
+/**
+ * {@link IRemoteTransition} for testing purposes.
+ * Stores info about
+ * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+ * IRemoteTransitionFinishedCallback)} being called.
+ */
+public class TestRemoteTransition extends IRemoteTransition.Stub {
+ private boolean mCalled = false;
+ final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
+
+ @Override
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
+ mCalled = true;
+ finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
+
+ /**
+ * Check whether this remote transition
+ * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+ * IRemoteTransitionFinishedCallback)} is called
+ */
+ public boolean isCalled() {
+ return mCalled;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index c6cccc0..664fbb2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -28,10 +28,10 @@
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
-import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DisplayAreaInfo
+import android.window.RemoteTransition
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
@@ -43,6 +43,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRemoteTransition
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
@@ -57,8 +58,10 @@
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isA
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito
@@ -174,9 +178,9 @@
markTaskHidden(task1)
markTaskHidden(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -192,9 +196,9 @@
markTaskVisible(task1)
markTaskVisible(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -210,9 +214,9 @@
markTaskHidden(task1)
markTaskVisible(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -224,9 +228,9 @@
fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
val homeTask = setUpHomeTask()
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(1)
wct.assertReorderAt(index = 0, homeTask)
}
@@ -240,9 +244,9 @@
markTaskHidden(taskDefaultDisplay)
markTaskHidden(taskSecondDisplay)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(2)
// Expect order to be from bottom: home, task
wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
@@ -373,7 +377,7 @@
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToFullscreen(task)
- val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ val wct = getLatestWct(type = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
@@ -383,7 +387,7 @@
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
controller.moveToFullscreen(task)
- val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ val wct = getLatestWct(type = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@@ -401,7 +405,7 @@
controller.moveToFullscreen(taskDefaultDisplay)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
}
@@ -414,7 +418,7 @@
controller.moveTaskToFront(task1)
- val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT)
+ val wct = getLatestWct(type = TRANSIT_TO_FRONT)
assertThat(wct.hierarchyOps).hasSize(1)
wct.assertReorderAt(index = 0, task1)
}
@@ -439,7 +443,7 @@
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(hierarchyOps).hasSize(1)
assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -461,7 +465,7 @@
val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(hierarchyOps).hasSize(1)
assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -747,11 +751,16 @@
}
private fun getLatestWct(
- @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+ handlerClass: Class<out TransitionHandler>? = null
): WindowContainerTransaction {
val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
if (ENABLE_SHELL_TRANSITIONS) {
- verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull())
+ if (handlerClass == null) {
+ verify(transitions).startTransition(eq(type), arg.capture(), isNull())
+ } else {
+ verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+ }
} else {
verify(shellTaskOrganizer).applyTransaction(arg.capture())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 5efd9ad..d542139 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -47,11 +47,8 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.os.IBinder;
-import android.os.RemoteException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import android.window.IRemoteTransition;
-import android.window.IRemoteTransitionFinishedCallback;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -65,6 +62,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRemoteTransition;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.common.DisplayController;
@@ -205,7 +203,7 @@
// Make sure split-screen is now visible
assertTrue(mStageCoordinator.isSplitScreenVisible());
- assertTrue(testRemote.mCalled);
+ assertTrue(testRemote.isCalled());
}
@Test
@@ -468,24 +466,4 @@
return out;
}
- class TestRemoteTransition extends IRemoteTransition.Stub {
- boolean mCalled = false;
- final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
-
- @Override
- public void startAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction startTransaction,
- IRemoteTransitionFinishedCallback finishCallback)
- throws RemoteException {
- mCalled = true;
- finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
- }
-
- @Override
- public void mergeAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
- }
-
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0088051..4afb29e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -43,6 +43,8 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
@@ -88,6 +90,10 @@
SyncTransactionQueue mSyncQueue;
@Mock
Transitions mTransitions;
+ @Mock
+ Looper mViewLooper;
+ @Mock
+ Handler mViewHandler;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -105,6 +111,8 @@
.build();
mContext = getContext();
+ doReturn(true).when(mViewLooper).isCurrentThread();
+ doReturn(mViewLooper).when(mViewHandler).getLooper();
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
@@ -132,6 +140,7 @@
mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
mTaskViewTransitions, mSyncQueue));
mTaskView = new TaskView(mContext, mTaskViewTaskController);
+ mTaskView.setHandler(mViewHandler);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -646,4 +655,17 @@
assertThat(mTaskViewTaskController.getTaskInfo()).isNull();
}
+
+ @Test
+ public void testOnTaskInfoChangedOnSameUiThread() {
+ mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+ verify(mViewHandler, never()).post(any());
+ }
+
+ @Test
+ public void testOnTaskInfoChangedOnDifferentUiThread() {
+ doReturn(false).when(mViewLooper).isCurrentThread();
+ mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+ verify(mViewHandler).post(any());
+ }
}
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 764d1ef..69fda34 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -166,11 +166,12 @@
#endif
mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
- mBounds)
+ SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds)
.mesh;
} else {
mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
- mBuilder->fUniforms, mBounds)
+ mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(),
+ mBounds)
.mesh;
}
mIsDirty = false;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 71f47e9..ff0d8d7 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -539,7 +539,7 @@
if (!cpuMesh.indexBuffer()) {
gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(),
cpuMesh.vertexOffset(), cpuMesh.refUniforms(),
- cpuMesh.bounds())
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
} else {
sk_sp<SkMesh::IndexBuffer> ib =
@@ -547,7 +547,8 @@
gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb,
cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib,
cpuMesh.indexCount(), cpuMesh.indexOffset(),
- cpuMesh.refUniforms(), cpuMesh.bounds())
+ cpuMesh.refUniforms(),
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index d1ebe6d..1c3399a 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -72,6 +72,7 @@
mSessionValid = true;
mHintSession = nullptr;
}
+ mResetsSinceLastReport = 0;
}
bool HintSessionWrapper::init() {
@@ -109,12 +110,13 @@
tids.push_back(mUiThreadId);
tids.push_back(mRenderThreadId);
- // Use a placeholder target value to initialize,
- // this will always be replaced elsewhere before it gets used
- int64_t defaultTargetDurationNanos = 16666667;
+ // Use the cached target value if there is one, otherwise use a default. This is to ensure
+ // the cached target and target in PowerHAL are consistent, and that it updates correctly
+ // whenever there is a change.
+ int64_t targetDurationNanos =
+ mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration;
mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
- return mBinding->createSession(manager, tids.data(), tids.size(),
- defaultTargetDurationNanos);
+ return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos);
});
return false;
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 36e91ea..41891cd 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -65,6 +65,7 @@
static constexpr nsecs_t kResetHintTimeout = 100_ms;
static constexpr int64_t kSanityCheckLowerBound = 100_us;
static constexpr int64_t kSanityCheckUpperBound = 10_s;
+ static constexpr int64_t kDefaultTargetDuration = 16666667;
// Allows easier stub when testing
class HintSessionBinding {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 4eabfb2..8445032 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -26,6 +26,21 @@
srcs: [
"PointerController_test.cpp",
],
+ sanitize: {
+ hwaddress: true,
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ },
+ },
shared_libs: [
"libandroid_runtime",
"libinputservice",
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 94faf4a..d9efd3c 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -148,6 +148,25 @@
latestPointerDisplayId = displayId;
}
+class TestPointerController : public PointerController {
+public:
+ TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+ sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper,
+ SpriteController& spriteController)
+ : PointerController(
+ policy, looper, spriteController,
+ /*enabled=*/true,
+ [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Register listener
+ registeredListener = listener;
+ },
+ [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Unregister listener
+ if (registeredListener == listener) registeredListener = nullptr;
+ }) {}
+ ~TestPointerController() override {}
+};
+
class PointerControllerTest : public Test {
protected:
PointerControllerTest();
@@ -159,6 +178,7 @@
sp<MockPointerControllerPolicyInterface> mPolicy;
std::unique_ptr<MockSpriteController> mSpriteController;
std::shared_ptr<PointerController> mPointerController;
+ sp<android::gui::WindowInfosListener> mRegisteredListener;
private:
void loopThread();
@@ -181,11 +201,12 @@
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController =
- PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
+ mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy,
+ mLooper, *mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
+ mPointerController.reset();
mRunning.store(false, std::memory_order_relaxed);
mThread.join();
}
@@ -316,31 +337,16 @@
class PointerControllerWindowInfoListenerTest : public Test {};
-class TestPointerController : public PointerController {
-public:
- TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
- const sp<Looper>& looper, SpriteController& spriteController)
- : PointerController(
- new MockPointerControllerPolicyInterface(), looper, spriteController,
- /*enabled=*/true,
- [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
- // Register listener
- registeredListener = listener;
- },
- [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
- // Unregister listener
- if (registeredListener == listener) registeredListener = nullptr;
- }) {}
-};
-
TEST_F(PointerControllerWindowInfoListenerTest,
doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
sp<Looper> looper = new Looper(false);
auto spriteController = NiceMock<MockSpriteController>(looper);
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
+ sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface();
{
- TestPointerController pointerController(registeredListener, looper, spriteController);
+ TestPointerController pointerController(registeredListener, policy, looper,
+ spriteController);
ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
localListenerCopy = registeredListener;
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9924fae..09f09b9 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -2409,13 +2409,16 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
+ acquireTRMSLock("openDescrambler()");
mDemuxLock.lock();
try {
- if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
+ // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired
+ if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) {
return null;
}
return requestDescrambler();
} finally {
+ releaseTRMSLock();
mDemuxLock.unlock();
}
}
diff --git a/mime/Android.bp b/mime/Android.bp
index a3ea65c..757862b 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -125,6 +124,6 @@
srcs: [
"java-res/vendor.mime.types",
],
- // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it
- cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
+ // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it
+ cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | (grep ' ' || echo -n '') | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
}
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 38d98a9..0d4af2a 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -12,6 +12,7 @@
manifest: "AndroidManifest.xml",
srcs: ["src/**/*.kt"],
static_libs: [
+ "androidx.activity_activity-compose",
"androidx.core_core-ktx",
"androidx.credentials_credentials",
"guava",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
new file mode 100644
index 0000000..6498ff7
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager
+
+const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index defba8d..8986e52 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -17,14 +17,25 @@
package com.android.credentialmanager
import android.content.Intent
+import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.mapper.toGet
import com.android.credentialmanager.mapper.toRequestCancel
+import com.android.credentialmanager.mapper.toRequestClose
import com.android.credentialmanager.model.Request
-fun Intent.parse(): Request {
- this.toRequestCancel()?.let { return it }
+fun Intent.parse(
+ packageManager: PackageManager,
+ previousIntent: Intent? = null,
+): Request {
+ this.toRequestClose(packageManager, previousIntent)?.let { closeRequest ->
+ return closeRequest
+ }
+
+ this.toRequestCancel(packageManager)?.let { cancelRequest ->
+ return cancelRequest
+ }
return when (requestInfo?.type) {
RequestInfo.TYPE_CREATE -> {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
new file mode 100644
index 0000000..ef083fd
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.credentialmanager.activity
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts
+
+/**
+ * A custom StartIntentSenderForResult contract implementation that attaches an [ActivityOptions]
+ * that opts in for background activity launch.
+ */
+class StartBalIntentSenderForResultContract :
+ ActivityResultContract<IntentSenderRequest, ActivityResult>() {
+ override fun createIntent(context: Context, input: IntentSenderRequest): Intent {
+ val activityOptionBundle =
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ ).toBundle()
+ return Intent(
+ ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST
+ ).putExtra(
+ ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE,
+ activityOptionBundle
+ ).putExtra(
+ ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST,
+ input
+ )
+ }
+
+ override fun parseResult(
+ resultCode: Int,
+ intent: Intent?
+ ): ActivityResult = ActivityResult(resultCode, intent)
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index a4c20bf..4533db6 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -18,10 +18,12 @@
import android.content.Intent
import android.credentials.ui.CancelUiRequest
+import android.credentials.ui.Constants
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
+import android.os.ResultReceiver
val Intent.cancelUiRequest: CancelUiRequest?
get() = this.extras?.getParcelable(
@@ -46,3 +48,9 @@
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
CreateCredentialProviderData::class.java
) ?: emptyList()
+
+val Intent.resultReceiver: ResultReceiver?
+ get() = this.getParcelableExtra(
+ Constants.EXTRA_RESULT_RECEIVER,
+ ResultReceiver::class.java
+ )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
index 86a6d23..555a86f 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -17,13 +17,23 @@
package com.android.credentialmanager.mapper
import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.ktx.appLabel
import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.model.Request
-fun Intent.toRequestCancel(): Request.Cancel? =
+fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
this.cancelUiRequest?.let { cancelUiRequest ->
- Request.Cancel(
- showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
- appPackageName = cancelUiRequest.appPackageName
- )
+ val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+ if (appLabel == null) {
+ Log.d(TAG, "Received UI cancel request with an invalid package name.")
+ null
+ } else {
+ Request.Cancel(
+ showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
+ appName = appLabel
+ )
+ }
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
new file mode 100644
index 0000000..6de3e7d
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.mapper
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.model.Request
+
+fun Intent.toRequestClose(
+ packageManager: PackageManager,
+ previousIntent: Intent? = null,
+): Request.Close? {
+ // Close request comes as "Cancel" request from Credential Manager API
+ val currentRequest = toRequestCancel(packageManager = packageManager) ?: return null
+
+ if (currentRequest.showCancellationUi) {
+ // Current request is to Cancel and not to Close
+ return null
+ }
+
+ previousIntent?.let {
+ val previousToken = previousIntent.requestInfo?.token
+ val currentToken = this.requestInfo?.token
+
+ if (previousToken != currentToken) {
+ // Current cancellation is for a different request, don't close the current flow.
+ return null
+ }
+ }
+
+ return Request.Close
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index ed9d563..ee45fbb 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -1,22 +1,66 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.mapper
import android.content.Intent
+import android.credentials.ui.Entry
import android.credentials.ui.GetCredentialProviderData
+import androidx.credentials.provider.PasswordCredentialEntry
+import com.android.credentialmanager.factory.fromSlice
import com.android.credentialmanager.ktx.getCredentialProviderDataList
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.ktx.resultReceiver
+import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
-fun Intent.toGet() = Request.Get(
- providers = ImmutableMap.copyOf(
- getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
- ),
- entries = ImmutableList.copyOf(
- getCredentialProviderDataList.map { providerData ->
- check(providerData is GetCredentialProviderData) {
- "Invalid provider data type for GetCredentialRequest"
+fun Intent.toGet(): Request.Get {
+ val credentialEntries = mutableListOf<Pair<String, Entry>>()
+ for (providerData in getCredentialProviderDataList) {
+ if (providerData is GetCredentialProviderData) {
+ for (credentialEntry in providerData.credentialEntries) {
+ credentialEntries.add(
+ Pair(providerData.providerFlattenedComponentName, credentialEntry)
+ )
}
- providerData
- }.flatMap { it.credentialEntries }
+ }
+ }
+
+ val passwordEntries = mutableListOf<Password>()
+ for ((providerId, entry) in credentialEntries) {
+ val slice = fromSlice(entry.slice)
+ if (slice is PasswordCredentialEntry) {
+ passwordEntries.add(
+ Password(
+ providerId = providerId,
+ entry = entry,
+ passwordCredentialEntry = slice
+ )
+ )
+ }
+ }
+
+ return Request.Get(
+ token = requestInfo?.token,
+ resultReceiver = this.resultReceiver,
+ providers = ImmutableMap.copyOf(
+ getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
+ ),
+ passwordEntries = ImmutableList.copyOf(passwordEntries)
)
-)
\ No newline at end of file
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
new file mode 100644
index 0000000..2fe4fd5
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.model
+
+import android.credentials.ui.Entry
+import androidx.credentials.provider.PasswordCredentialEntry
+
+data class Password(
+ val providerId: String,
+ val entry: Entry,
+ val passwordCredentialEntry: PasswordCredentialEntry,
+)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index bc07310..6011a1c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,8 +16,9 @@
package com.android.credentialmanager.model
-import android.credentials.ui.Entry
import android.credentials.ui.ProviderData
+import android.os.IBinder
+import android.os.ResultReceiver
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
@@ -25,15 +26,33 @@
* Represents the request made by the CredentialManager API.
*/
sealed class Request {
+
+ /**
+ * Request to close the app without displaying a message to the user and without reporting
+ * anything back to the Credential Manager service.
+ */
+ data object Close : Request()
+
+ /**
+ * Request to close the app, displaying a message to the user.
+ */
data class Cancel(
val showCancellationUi: Boolean,
- val appPackageName: String?
+ val appName: String
) : Request()
+ /**
+ * Request to start the get credentials flow.
+ */
data class Get(
+ val token: IBinder?,
+ val resultReceiver: ResultReceiver?,
val providers: ImmutableMap<String, ProviderData>,
- val entries: ImmutableList<Entry>,
+ val passwordEntries: ImmutableList<Password>,
) : Request()
+ /**
+ * Request to start the create credentials flow.
+ */
data object Create : Request()
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
new file mode 100644
index 0000000..5ab5ab9
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.repository
+
+import android.app.Application
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.parse
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class RequestRepository(
+ private val application: Application,
+) {
+
+ private val _requests = MutableStateFlow<Request?>(null)
+ val requests: StateFlow<Request?> = _requests
+
+ suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
+ val request = intent.parse(
+ packageManager = application.packageManager,
+ previousIntent = previousIntent
+ )
+
+ Log.d(TAG, "Request parsed: $request")
+
+ _requests.value = request
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 943c2b4..ba88484 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,23 +16,183 @@
package com.android.credentialmanager.autofill
+import android.app.assist.AssistStructure
+import android.content.Context
+import android.credentials.GetCredentialRequest
+import android.credentials.CredentialManager
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.os.Bundle
import android.os.CancellationSignal
-import android.service.autofill.AutofillService
-import android.service.autofill.FillCallback
+import android.os.OutcomeReceiver
import android.service.autofill.FillRequest
+import android.service.autofill.AutofillService
+import android.service.autofill.FillResponse
+import android.service.autofill.FillCallback
import android.service.autofill.SaveRequest
import android.service.autofill.SaveCallback
+import android.util.Log
+import org.json.JSONObject
+import java.util.concurrent.Executors
class CredentialAutofillService : AutofillService() {
+
+ companion object {
+ private const val TAG = "CredAutofill"
+
+ private const val CRED_HINT_PREFIX = "credential="
+ private const val REQUEST_DATA_KEY = "requestData"
+ private const val CANDIDATE_DATA_KEY = "candidateQueryData"
+ private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
+ private const val CRED_OPTIONS_KEY = "credentialOptions"
+ private const val TYPE_KEY = "type"
+ }
+
+ private val credentialManager: CredentialManager =
+ getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
+
override fun onFillRequest(
request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
+ val context = request.fillContexts
+ val structure = context[context.size - 1].structure
+ val callingPackage = structure.activityComponent.packageName
+ Log.i(TAG, "onFillRequest called for $callingPackage")
+
+ val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
+ if (getCredRequest == null) {
+ callback.onFailure("No credential manager request found")
+ return
+ }
+
+ val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> {
+ override fun onResult(result: GetCandidateCredentialsResponse) {
+ Log.i(TAG, "getCandidateCredentials onResponse")
+ val fillResponse: FillResponse? = convertToFillResponse(result, request)
+ callback.onSuccess(fillResponse)
+ }
+
+ override fun onError(error: GetCandidateCredentialsException) {
+ Log.i(TAG, "getCandidateCredentials onError")
+ callback.onFailure("error received from credential manager ${error.message}")
+ }
+ }
+
+ credentialManager.getCandidateCredentials(
+ getCredRequest,
+ callingPackage,
+ CancellationSignal(),
+ Executors.newSingleThreadExecutor(),
+ outcome
+ )
+ }
+
+ private fun convertToFillResponse(
+ getCredResponse: GetCandidateCredentialsResponse,
+ filLRequest: FillRequest
+ ): FillResponse? {
TODO("Not yet implemented")
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
TODO("Not yet implemented")
}
+
+ private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? {
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ traverseStructure(structure, credentialOptions)
+
+ if (credentialOptions.isNotEmpty()) {
+ return GetCredentialRequest.Builder(Bundle.EMPTY)
+ .setCredentialOptions(credentialOptions)
+ .build()
+ }
+ return null
+ }
+
+ private fun traverseStructure(
+ structure: AssistStructure,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val windowNodes: List<AssistStructure.WindowNode> =
+ structure.run {
+ (0 until windowNodeCount).map { getWindowNodeAt(it) }
+ }
+
+ windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
+ traverseNode(windowNode.rootViewNode, cmRequests)
+ }
+ }
+
+ private fun traverseNode(
+ viewNode: AssistStructure.ViewNode?,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val options = getCredentialOptionsFromViewNode(viewNode)
+ cmRequests.addAll(options)
+
+ val children: List<AssistStructure.ViewNode>? =
+ viewNode?.run {
+ (0 until childCount).map { getChildAt(it) }
+ }
+
+ children?.forEach { childNode: AssistStructure.ViewNode ->
+ traverseNode(childNode, cmRequests)
+ }
+ }
+
+ private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
+ List<CredentialOption> {
+ // TODO(b/293945193) Replace with isCredential check from viewNode
+ val credentialHints: MutableList<String> = mutableListOf()
+ if (viewNode != null && viewNode.autofillHints != null) {
+ for (hint in viewNode.autofillHints!!) {
+ if (hint.startsWith(CRED_HINT_PREFIX)) {
+ credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
+ }
+ }
+ }
+
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ for (credentialHint in credentialHints) {
+ convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+ // TODO(b/302000646) Move this logic to jetpack so that is consistent
+ // with building the json
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+
+ val json = JSONObject(jsonString)
+ val options = json.getJSONArray(CRED_OPTIONS_KEY)
+ for (i in 0 until options.length()) {
+ val option = options.getJSONObject(i)
+
+ credentialOptions.add(CredentialOption(
+ option.getString(TYPE_KEY),
+ convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
+ convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+ option.getBoolean(SYS_PROVIDER_REQ_KEY),
+ ))
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToBundle(json: JSONObject): Bundle {
+ val result = Bundle()
+ json.keys().forEach {
+ val v = json.get(it)
+ when (v) {
+ is String -> result.putString(it, v)
+ is Boolean -> result.putBoolean(it, v)
+ }
+ }
+ return result
+ }
}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c0dff16..e5f5cc2 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -37,6 +37,7 @@
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-runtime-compose",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"androidx.wear.compose_compose-foundation",
"androidx.wear.compose_compose-material",
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 90248734..b480ac3 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
/*
* Copyright (c) 2023 Google Inc.
@@ -21,25 +22,27 @@
<uses-feature android:name="android.hardware.type.watch" />
- <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
- <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+ <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
<application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:label="@string/app_name"
- android:supportsRtl="true">
+ android:name=".CredentialSelectorApp"
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:label="@string/app_name"
+ android:supportsRtl="true">
+ <!-- Activity called by GMS has to be exactly:
+ com.android.credentialmanager.CredentialSelectorActivity -->
<activity
- android:name=".ui.CredentialSelectorActivity"
+ android:name=".CredentialSelectorActivity"
+ android:excludeFromRecents="true"
android:exported="true"
- android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
- android:launchMode="singleTop"
android:label="@string/app_name"
- android:excludeFromRecents="true">
- </activity>
- </application>
+ android:launchMode="singleTop"
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+ </application>
</manifest>
diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/packages/CredentialManager/wear/res/values/themes.xml
deleted file mode 100644
index 22329e9f..0000000
--- a/packages/CredentialManager/wear/res/values/themes.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<resources>
- <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowIsTranslucent">true</item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
new file mode 100644
index 0000000..273d0b1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.ui.WearApp
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import kotlinx.coroutines.launch
+
+class CredentialSelectorActivity : ComponentActivity() {
+
+ private val viewModel: CredentialSelectorViewModel by viewModels {
+ CredentialSelectorViewModel.Factory
+ }
+
+ @OptIn(ExperimentalHorologistApi::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setTheme(android.R.style.Theme_DeviceDefault)
+
+ // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
+ // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
+ // contents of the "if" bracket.
+ if (false) {
+ setContent {
+ MaterialTheme {
+ WearApp(
+ viewModel = viewModel,
+ onCloseApp = ::finish,
+ )
+ }
+ }
+ } else {
+ // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.uiState.collect { uiState ->
+ when (uiState) {
+ CredentialSelectorUiState.Idle -> {
+ // Don't display anything, assuming that there should be minimal latency
+ // to parse the Credential Manager intent and define the state of the
+ // app. If latency is big, then a "loading" screen should be displayed
+ // to the user.
+ }
+
+ is CredentialSelectorUiState.Get -> {
+ setContent {
+ MaterialTheme {
+ SinglePasswordScreen(
+ columnState = belowTimeTextPreview(),
+ onCloseApp = ::finish,
+ )
+ }
+ }
+ }
+
+ else -> finish()
+ }
+ }
+ }
+ }
+ }
+
+ viewModel.onNewIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+
+ val previousIntent = getIntent()
+ setIntent(intent)
+
+ viewModel.onNewIntent(intent, previousIntent)
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
new file mode 100644
index 0000000..7c81fd0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager
+
+import android.app.Application
+import com.android.credentialmanager.di.inject
+import com.android.credentialmanager.repository.RequestRepository
+
+class CredentialSelectorApp : Application() {
+
+ lateinit var requestRepository: RequestRepository
+
+ override fun onCreate() {
+ super.onCreate()
+
+ inject()
+ }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
new file mode 100644
index 0000000..d557dc0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager
+
+import android.content.Intent
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.mappers.toGet
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class CredentialSelectorViewModel(
+ private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+ val uiState: StateFlow<CredentialSelectorUiState> = requestRepository.requests
+ .map { request ->
+ when (request) {
+ null -> CredentialSelectorUiState.Idle
+ is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
+ Request.Close -> CredentialSelectorUiState.Close
+ Request.Create -> CredentialSelectorUiState.Create
+ is Request.Get -> request.toGet()
+ }
+ }
+ .stateIn(
+ viewModelScope,
+ started = SharingStarted.WhileSubscribed(5000),
+ initialValue = CredentialSelectorUiState.Idle,
+ )
+
+ fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
+ viewModelScope.launch {
+ requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
+ }
+ }
+
+ companion object {
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ modelClass: Class<T>,
+ extras: CreationExtras
+ ): T {
+ val application = checkNotNull(extras[APPLICATION_KEY])
+
+ return CredentialSelectorViewModel(
+ requestRepository = (application as CredentialSelectorApp).requestRepository,
+ ) as T
+ }
+ }
+ }
+}
+
+sealed class CredentialSelectorUiState {
+ data object Idle : CredentialSelectorUiState()
+ sealed class Get : CredentialSelectorUiState() {
+ data object SingleProviderSinglePasskey : Get()
+ data object SingleProviderSinglePassword : Get()
+
+ // TODO: b/301206470 add the remaining states
+ }
+
+ data object Create : CredentialSelectorUiState()
+ data class Cancel(val appName: String) : CredentialSelectorUiState()
+ data object Close : CredentialSelectorUiState()
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
new file mode 100644
index 0000000..a11017b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
@@ -0,0 +1,17 @@
+package com.android.credentialmanager.di
+
+import android.app.Application
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.repository.RequestRepository
+
+// TODO b/301601582 add Hilt for dependency injection
+
+fun CredentialSelectorApp.inject() {
+ requestRepository = requestRepository(application = this)
+}
+
+private fun requestRepository(
+ application: Application,
+): RequestRepository = RequestRepository(
+ application = application,
+)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
deleted file mode 100644
index 53122ba..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.0N
- *
- * 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.credentialmanager.ui
-
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.wear.compose.material.MaterialTheme
-import kotlinx.coroutines.launch
-
-class CredentialSelectorActivity : ComponentActivity() {
-
- private val viewModel: CredentialSelectorViewModel by viewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setTheme(android.R.style.Theme_DeviceDefault)
-
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.uiState.collect { uiState ->
- when (uiState) {
- CredentialSelectorUiState.Idle -> {
- // Don't display anything, assuming that there should be minimal latency
- // to parse the Credential Manager intent and define the state of the
- // app. If latency is big, then a "loading" screen should be displayed
- // to the user.
- }
-
- is CredentialSelectorUiState.Get -> {
- setContent {
- MaterialTheme {
- WearApp()
- }
- }
- }
-
- CredentialSelectorUiState.Create -> {
- // TODO: b/301206624 - Implement create flow
- finish()
- }
-
- is CredentialSelectorUiState.Cancel -> {
- // TODO: b/300422310 - Implement cancel with message flow
- finish()
- }
-
- CredentialSelectorUiState.Finish -> {
- finish()
- }
- }
- }
- }
- }
-
- viewModel.onNewIntent(intent)
- }
-
- override fun onNewIntent(intent: Intent) {
- super.onNewIntent(intent)
-
- val previousIntent = getIntent()
- setIntent(intent)
-
- viewModel.onNewIntent(intent, previousIntent)
- }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
deleted file mode 100644
index d22d5d1..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.0N
- *
- * 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.credentialmanager.ui
-
-import android.app.Application
-import android.content.Intent
-import android.util.Log
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.parse
-import com.android.credentialmanager.ktx.appLabel
-import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.mapper.toGet
-import com.android.credentialmanager.ui.model.PasskeyUiModel
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.ui.mapper.toGet
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class CredentialSelectorViewModel(
- private val application: Application
-) : AndroidViewModel(application = application) {
-
- private val _uiState =
- MutableStateFlow<CredentialSelectorUiState>(CredentialSelectorUiState.Idle)
- val uiState: StateFlow<CredentialSelectorUiState> = _uiState
-
- fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
- viewModelScope.launch {
- val request = intent.parse()
- if (shouldFinishActivity(request = request, previousIntent = previousIntent)) {
- _uiState.value = CredentialSelectorUiState.Finish
- } else {
- when (request) {
- is Request.Cancel -> {
- request.appPackageName?.let { appPackageName ->
- application.packageManager.appLabel(appPackageName)?.let { appLabel ->
- _uiState.value = CredentialSelectorUiState.Cancel(appLabel)
- } ?: run {
- Log.d(TAG,
- "Received UI cancel request with an invalid package name.")
- _uiState.value = CredentialSelectorUiState.Finish
- }
- } ?: run {
- Log.d(TAG, "Received UI cancel request with an invalid package name.")
- _uiState.value = CredentialSelectorUiState.Finish
- }
- }
-
- Request.Create -> {
- _uiState.value = CredentialSelectorUiState.Create
- }
-
- is Request.Get -> {
- _uiState.value = request.toGet()
- }
- }
- }
- }
- }
-
- /**
- * Check if backend requested the UI activity to be cancelled. Different from the other
- * finishing flows, this one does not report anything back to the Credential Manager service
- * backend.
- */
- private fun shouldFinishActivity(request: Request, previousIntent: Intent? = null): Boolean {
- if (request !is Request.Cancel) {
- return false
- } else {
- Log.d(
- TAG, "Received UI cancellation intent. Should show cancellation" +
- " ui = ${request.showCancellationUi}")
-
- previousIntent?.let {
- val previousUiRequest = previousIntent.parse()
-
- if (previousUiRequest is Request.Cancel) {
- val previousToken = previousIntent.requestInfo?.token
- val currentToken = previousIntent.requestInfo?.token
-
- if (previousToken != currentToken) {
- // Cancellation was for a different request, don't cancel the current UI.
- return false
- }
- }
- }
-
- return !request.showCancellationUi
- }
- }
-}
-
-sealed class CredentialSelectorUiState {
- data object Idle : CredentialSelectorUiState()
- sealed class Get : CredentialSelectorUiState() {
- data class SingleProviderSinglePasskey(val passkeyUiModel: PasskeyUiModel) : Get()
- data class SingleProviderSinglePassword(val passwordUiModel: PasswordUiModel) : Get()
-
- // TODO: b/301206470 add the remaining states
- }
-
- data object Create : CredentialSelectorUiState()
- data class Cancel(val appName: String) : CredentialSelectorUiState()
- data object Finish : CredentialSelectorUiState()
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
new file mode 100644
index 0000000..da5697d
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui
+
+import androidx.navigation.NavController
+
+fun NavController.navigateToLoading() {
+ navigate(Screen.Loading.route)
+}
+
+fun NavController.navigateToSinglePasswordScreen() {
+ navigate(Screen.SinglePasswordScreen.route)
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
index 7d1a49b..c3919a0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -19,5 +19,7 @@
sealed class Screen(
val route: String,
) {
- data object Main : Screen("main")
+ data object Loading : Screen("loading")
+
+ data object SinglePasswordScreen : Screen("singlePasswordScreen")
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 19ea9ed..7e0ea30 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -19,28 +19,94 @@
package com.android.credentialmanager.ui
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
-import com.android.credentialmanager.ui.screens.MainScreen
+import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.ui.screens.LoadingScreen
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.navscaffold.WearNavScaffold
import com.google.android.horologist.compose.navscaffold.composable
+import com.google.android.horologist.compose.navscaffold.scrollable
@Composable
-fun WearApp() {
+fun WearApp(
+ viewModel: CredentialSelectorViewModel,
+ onCloseApp: () -> Unit,
+) {
val navController = rememberSwipeDismissableNavController()
val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
val navHostState =
rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
WearNavScaffold(
- startDestination = Screen.Main.route,
+ startDestination = Screen.Loading.route,
navController = navController,
state = navHostState,
) {
- composable(Screen.Main.route) {
- MainScreen()
+ composable(Screen.Loading.route) {
+ LoadingScreen()
+ }
+
+ scrollable(Screen.SinglePasswordScreen.route) {
+ SinglePasswordScreen(
+ columnState = it.columnState,
+ onCloseApp = onCloseApp,
+ )
+ }
+ }
+
+ when (val state = uiState) {
+ CredentialSelectorUiState.Idle -> {
+ if (navController.currentDestination?.route != Screen.Loading.route) {
+ navController.navigateToLoading()
+ }
+ }
+
+ is CredentialSelectorUiState.Get -> {
+ handleGetNavigation(
+ navController = navController,
+ state = state,
+ onCloseApp = onCloseApp,
+ )
+ }
+
+ CredentialSelectorUiState.Create -> {
+ // TODO: b/301206624 - Implement create flow
+ onCloseApp()
+ }
+
+ is CredentialSelectorUiState.Cancel -> {
+ // TODO: b/300422310 - Implement cancel with message flow
+ onCloseApp()
+ }
+
+ CredentialSelectorUiState.Close -> {
+ onCloseApp()
+ }
+ }
+}
+
+private fun handleGetNavigation(
+ navController: NavController,
+ state: CredentialSelectorUiState.Get,
+ onCloseApp: () -> Unit,
+) {
+ when (state) {
+ is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+ navController.navigateToSinglePasswordScreen()
+ }
+
+ else -> {
+ // TODO: b/301206470 - Implement other get flows
+ onCloseApp()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
deleted file mode 100644
index 5ceec178..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.credentialmanager.ui.mapper
-
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
-import com.android.credentialmanager.ui.CredentialSelectorUiState
-import com.android.credentialmanager.factory.fromSlice
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
- if (this.providers.isEmpty()) {
- throw IllegalStateException("Invalid GetCredential request with empty list of providers.")
- }
-
- if (this.entries.isEmpty()) {
- throw IllegalStateException("Invalid GetCredential request with empty list of entries.")
- }
-
- if (this.providers.size == 1) {
- if (this.entries.size == 1) {
- val slice = this.entries.first().slice
- when (val credentialEntry = fromSlice(slice)) {
- is PasswordCredentialEntry -> {
- return CredentialSelectorUiState.Get.SingleProviderSinglePassword(
- PasswordUiModel(credentialEntry.displayName.toString())
- )
- }
-
- is PublicKeyCredentialEntry -> {
- TODO("b/301206470 - to be implemented")
- }
-
- is CustomCredentialEntry -> {
- TODO("b/301206470 - to be implemented")
- }
-
- else -> {
- throw IllegalStateException(
- "Encountered unrecognized credential entry (${slice.spec?.type}) for " +
- "GetCredential request with single account"
- )
- }
- }
- } else {
- TODO("b/301206470 - to be implemented")
- }
- } else {
- TODO("b/301206470 - to be implemented")
- }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
new file mode 100644
index 0000000..f2f878e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui.mappers
+
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.CredentialSelectorUiState
+
+fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+ // TODO: b/301206470 returning a hard coded state for MVP
+ if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
+
+ return if (providers.size == 1) {
+ if (passwordEntries.size == 1) {
+ CredentialSelectorUiState.Get.SingleProviderSinglePassword
+ } else {
+ TODO() // b/301206470 - Implement other get flows
+ }
+ } else {
+ TODO() // b/301206470 - Implement other get flows
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
similarity index 74%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
index 94a671e..b3ab0c4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
@@ -16,17 +16,15 @@
package com.android.credentialmanager.ui.screens
-import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.wear.compose.material.Text
@Composable
-fun MainScreen(
+fun LoadingScreen(
modifier: Modifier = Modifier
) {
- Box(modifier = modifier, contentAlignment = Alignment.Center) {
- Text("This is a placeholder for the main screen.")
- }
+ // Don't display anything, assuming that there should be minimal latency
+ // to parse the Credential Manager intent and define the state of the
+ // app. If latency is big, then a "loading" screen should be displayed
+ // to the user.
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
deleted file mode 100644
index d863d3c..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 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
- *
- * https://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.
- */
-
-@file:OptIn(ExperimentalHorologistApi::class)
-
-package com.android.credentialmanager.ui.screens
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import com.android.credentialmanager.R
-import com.android.credentialmanager.ui.components.DialogButtonsRow
-import com.android.credentialmanager.ui.components.PasswordRow
-import com.android.credentialmanager.ui.components.SignInHeader
-import com.google.android.horologist.annotations.ExperimentalHorologistApi
-import com.google.android.horologist.compose.layout.ScalingLazyColumnState
-import com.google.android.horologist.compose.layout.belowTimeTextPreview
-import com.google.android.horologist.compose.tools.WearPreview
-
-@Composable
-fun SinglePasswordScreen(
- email: String,
- onCancelClick: () -> Unit,
- onOKClick: () -> Unit,
- columnState: ScalingLazyColumnState,
- modifier: Modifier = Modifier,
-) {
- SingleAccountScreen(
- headerContent = {
- SignInHeader(
- icon = R.drawable.passkey_icon,
- title = stringResource(R.string.use_password_title),
- )
- },
- accountContent = {
- PasswordRow(
- email = email,
- modifier = Modifier.padding(top = 10.dp),
- )
- },
- columnState = columnState,
- modifier = modifier.padding(horizontal = 10.dp)
- ) {
- item {
- DialogButtonsRow(
- onCancelClick = onCancelClick,
- onOKClick = onOKClick,
- modifier = Modifier.padding(top = 10.dp)
- )
- }
- }
-}
-
-@WearPreview
-@Composable
-fun SinglePasswordScreenPreview() {
- SinglePasswordScreen(
- email = "beckett_bakery@gmail.com",
- onCancelClick = {},
- onOKClick = {},
- columnState = belowTimeTextPreview(),
- )
-}
-
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
similarity index 97%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
index f344ad0..8532783 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalHorologistApi::class)
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
similarity index 94%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index c8f871e..c9b0230 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalHorologistApi::class)
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single.passkey
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
@@ -27,6 +27,7 @@
import com.android.credentialmanager.ui.components.AccountRow
import com.android.credentialmanager.ui.components.DialogButtonsRow
import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.google.android.horologist.compose.layout.belowTimeTextPreview
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
new file mode 100644
index 0000000..c885ec4
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 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
+ *
+ * https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens.single.password
+
+import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.PasswordRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SinglePasswordScreen(
+ columnState: ScalingLazyColumnState,
+ onCloseApp: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: SinglePasswordScreenViewModel =
+ viewModel(factory = SinglePasswordScreenViewModel.Factory),
+) {
+ viewModel.initialize()
+
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
+ when (val state = uiState) {
+ SinglePasswordScreenUiState.Idle -> {
+ // TODO: b/301206470 implement latency version of the screen
+ }
+
+ is SinglePasswordScreenUiState.Loaded -> {
+ val model = state.passwordUiModel
+ SinglePasswordScreen(
+ email = model.email,
+ onCancelClick = viewModel::onCancelClick,
+ onOKClick = viewModel::onOKClick,
+ columnState = columnState,
+ modifier = modifier
+ )
+ }
+
+ is SinglePasswordScreenUiState.PasswordSelected -> {
+ val launcher = rememberLauncherForActivityResult(
+ StartBalIntentSenderForResultContract()
+ ) {
+ viewModel.onPasswordInfoRetrieved(it.resultCode, it.data)
+ }
+
+ SideEffect {
+ launcher.launch(state.intentSenderRequest)
+ }
+ }
+
+ SinglePasswordScreenUiState.Cancel -> {
+ // TODO: b/301206470 implement navigation for when user taps cancel
+ }
+
+ SinglePasswordScreenUiState.Error -> {
+ // TODO: b/301206470 implement navigation for when there is an error to load screen
+ }
+
+ SinglePasswordScreenUiState.Completed -> {
+ Log.d(TAG, "Received signal to finish the activity.")
+ onCloseApp()
+ }
+ }
+}
+
+@Composable
+fun SinglePasswordScreen(
+ email: String,
+ onCancelClick: () -> Unit,
+ onOKClick: () -> Unit,
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+) {
+ SingleAccountScreen(
+ headerContent = {
+ SignInHeader(
+ icon = R.drawable.passkey_icon,
+ title = stringResource(R.string.use_password_title),
+ )
+ },
+ accountContent = {
+ PasswordRow(
+ email = email,
+ modifier = Modifier.padding(top = 10.dp),
+ )
+ },
+ columnState = columnState,
+ modifier = modifier.padding(horizontal = 10.dp)
+ ) {
+ item {
+ DialogButtonsRow(
+ onCancelClick = onCancelClick,
+ onOKClick = onOKClick,
+ modifier = Modifier.padding(top = 10.dp)
+ )
+ }
+ }
+}
+
+@WearPreview
+@Composable
+fun SinglePasswordScreenPreview() {
+ SinglePasswordScreen(
+ email = "beckett_bakery@gmail.com",
+ onCancelClick = {},
+ onOKClick = {},
+ columnState = belowTimeTextPreview(),
+ )
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
new file mode 100644
index 0000000..9b06622
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.0N
+ *
+ * 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.credentialmanager.ui.screens.single.password
+
+import android.content.Intent
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.ProviderPendingIntentResponse
+import android.credentials.ui.UserSelectionDialogResult
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.annotation.MainThread
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+class SinglePasswordScreenViewModel(
+ private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+ private var initializeCalled = false
+
+ private lateinit var requestGet: Request.Get
+ private lateinit var password: Password
+
+ private val _uiState =
+ MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
+ val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
+
+ @MainThread
+ fun initialize() {
+ if (initializeCalled) return
+ initializeCalled = true
+
+ viewModelScope.launch {
+ val request = requestRepository.requests.first()
+ Log.d(TAG, "request: $request")
+
+ if (request !is Request.Get) {
+ _uiState.value = SinglePasswordScreenUiState.Error
+ } else {
+ requestGet = request
+ if (requestGet.passwordEntries.isEmpty()) {
+ Log.d(TAG, "Empty passwordEntries")
+ _uiState.value = SinglePasswordScreenUiState.Error
+ } else {
+ password = requestGet.passwordEntries.first()
+ _uiState.value = SinglePasswordScreenUiState.Loaded(
+ PasswordUiModel(
+ email = password.passwordCredentialEntry.username.toString(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ fun onCancelClick() {
+ _uiState.value = SinglePasswordScreenUiState.Cancel
+ }
+
+ fun onOKClick() {
+ // TODO: b/301206470 move this code to shared module
+ val entryIntent = password.entry.frameworkExtrasIntent
+ entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, false)
+ val intentSenderRequest = IntentSenderRequest.Builder(
+ pendingIntent = password.passwordCredentialEntry.pendingIntent
+ ).setFillInIntent(entryIntent).build()
+
+ _uiState.value = SinglePasswordScreenUiState.PasswordSelected(
+ intentSenderRequest = intentSenderRequest
+ )
+ }
+
+ fun onPasswordInfoRetrieved(
+ resultCode: Int? = null,
+ resultData: Intent? = null,
+ ) {
+ // TODO: b/301206470 move this code to shared module
+ Log.d(TAG, "credential selected: {provider=${password.providerId}" +
+ ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
+
+ val userSelectionDialogResult = UserSelectionDialogResult(
+ requestGet.token,
+ password.providerId,
+ password.entry.key,
+ password.entry.subkey,
+ if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+ )
+ val resultDataBundle = Bundle()
+ UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+ requestGet.resultReceiver?.send(
+ BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+ resultDataBundle
+ )
+
+ _uiState.value = SinglePasswordScreenUiState.Completed
+ }
+
+ companion object {
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ modelClass: Class<T>,
+ extras: CreationExtras
+ ): T {
+ val application = checkNotNull(extras[APPLICATION_KEY])
+
+ return SinglePasswordScreenViewModel(
+ requestRepository = (application as CredentialSelectorApp).requestRepository,
+ ) as T
+ }
+ }
+ }
+}
+
+sealed class SinglePasswordScreenUiState {
+ data object Idle : SinglePasswordScreenUiState()
+ data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
+ data class PasswordSelected(
+ val intentSenderRequest: IntentSenderRequest
+ ) : SinglePasswordScreenUiState()
+
+ data object Cancel : SinglePasswordScreenUiState()
+ data object Error : SinglePasswordScreenUiState()
+ data object Completed : SinglePasswordScreenUiState()
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5e7e044..104f3d2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -235,6 +235,9 @@
srcs: [
/* Status bar fakes */
"tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt",
"tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9bfc4be..b5b873c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -634,7 +634,7 @@
<!-- started from MediaProjectionManager -->
<activity
- android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
+ android:name=".media.MediaProjectionPermissionActivity"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
@@ -643,7 +643,7 @@
android:visibleToInstantApps="true"/>
<activity
- android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
+ android:name=".media.MediaProjectionAppSelectorActivity"
android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index af6fa86..5c2f979 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -131,5 +131,22 @@
}
]
}
+ ],
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 8e34008..519c0a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -39,7 +39,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
@@ -166,11 +168,18 @@
modifier =
Modifier.align(Alignment.CenterVertically)
// use graphicsLayer instead of Modifier.scale to anchor transform to
- // top left corner
+ // the (start, top) corner
.graphicsLayer(
scaleX = 2.57f,
scaleY = 2.57f,
- transformOrigin = TransformOrigin(0f, 0.5f)
+ transformOrigin =
+ TransformOrigin(
+ when (LocalLayoutDirection.current) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
),
)
Spacer(modifier = Modifier.weight(1f))
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
index 7ce1ba3..d7d75d4 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -20,17 +20,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:paddingMode="stack" >
+ <!-- The groove used for indicating max volume !-->
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <inset
- android:insetLeft="@dimen/rounded_slider_track_inset"
- android:insetRight="@dimen/rounded_slider_track_inset" >
- <shape>
- <size android:height="@dimen/volume_dialog_track_width" />
- <corners android:radius="@dimen/volume_dialog_track_corner_radius" />
- <solid android:color="?androidprv:attr/colorAccentSecondaryVariant" />
- </shape>
- </inset>
+ <shape>
+ <size android:height="@dimen/volume_dialog_track_width" />
+ <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+ <solid android:color="?androidprv:attr/materialColorOutlineVariant" />
+ </shape>
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 37b8ae0..c70f8e2 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -22,8 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:paddingStart="4dp"
- android:paddingEnd="4dp"
->
+ android:paddingEnd="4dp">
<LinearLayout
android:id="@+id/half_shelf"
@@ -82,11 +81,21 @@
android:theme="@style/MainSwitch.Settingslib"/>
</com.android.systemui.statusbar.notification.row.AppControlView>
- <!-- ChannelRows get added dynamically -->
-
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_blocker_channel_list_height"
+ android:clipToPadding="false">
+ <LinearLayout
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <!-- ChannelRows get added dynamically -->
+ </LinearLayout>
+ </ScrollView>
</com.android.systemui.statusbar.notification.row.ChannelEditorListView>
- <RelativeLayout
+ <LinearLayout
android:id="@+id/bottom_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -98,25 +107,23 @@
android:text="@string/see_more_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
style="@style/Widget.Dialog.Button"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
<TextView
android:id="@+id/done_button"
android:text="@string/inline_ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:gravity="end|center_vertical"
android:maxWidth="125dp"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
- android:layout_alignParentEnd="true"
style="@style/Widget.Dialog.Button"/>
- </RelativeLayout>
+ </LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 259b9ad..cfb4017 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -89,6 +89,9 @@
<dimen name="global_actions_button_size">72dp</dimen>
<dimen name="global_actions_button_padding">26dp</dimen>
+ <!-- scroll view the size of 2 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">128dp</dimen>
+
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
<dimen name="lock_icon_margin_bottom">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88726af..0ee5da2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -313,6 +313,8 @@
<!-- The space around a notification menu item -->
<dimen name="notification_menu_icon_padding">20dp</dimen>
+ <!-- scroll view the size of 3 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">192dp</dimen>
<!-- The vertical space around the buttons in the inline settings -->
<dimen name="notification_guts_button_spacing">12dp</dimen>
@@ -545,7 +547,7 @@
<!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 -->
<dimen name="volume_slider_icon_inset">11dp</dimen>
- <dimen name="volume_dialog_track_width">4dp</dimen>
+ <dimen name="volume_dialog_track_width">40dp</dimen>
<dimen name="volume_dialog_track_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
index 96a974d..7b2e1af 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
@@ -19,6 +19,12 @@
import android.os.Trace
import android.os.TraceNameSupplier
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
/**
* Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
@@ -85,5 +91,18 @@
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, cookie)
}
}
+
+ /**
+ * Convenience method to avoid one indentation level when we want to add a trace when
+ * launching a coroutine
+ */
+ fun <T> CoroutineScope.tracedAsync(
+ method: String,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend () -> T
+ ): Deferred<T> {
+ return async(context, start) { traceAsync(method) { block() } }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 46d3c8a..79d9c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -1096,8 +1096,19 @@
// cancel the fingerprint scan.
mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
AOD_SEND_FINGER_UP_DELAY_MILLIS);
- // using a hard-coded value for major and minor until it is available from the sensor
- onFingerDown(requestId, screenX, screenY, minor, major);
+ // using a hard-coded value for orientation, time and gestureStart until they are
+ // available from the sensor.
+ onFingerDown(
+ requestId,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ screenX,
+ screenY,
+ minor,
+ major,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ true /* isAod */);
};
if (mScreenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index bae0ac7..f3a463b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -235,17 +235,16 @@
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */
- fun hide(
- loggingReason: String,
- ) {
+ /** Notifies the interactor that the input method editor has been hidden. */
+ fun onImeHidden() {
+ // If the bouncer is showing, hide it and return to the lockscreen scene.
if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
return
}
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
- loggingReason = loggingReason,
+ loggingReason = "IME hidden",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 0b0a8f5..66c6162 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -78,10 +78,7 @@
*/
fun onImeVisibilityChanged(isVisible: Boolean) {
if (isImeVisible && !isVisible) {
- // The IME has gone from visible to invisible, dismiss the bouncer.
- interactor.hide(
- loggingReason = "IME hidden",
- )
+ interactor.onImeHidden()
}
isImeVisible = isVisible
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4b38267..3b70555 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -298,6 +298,11 @@
@JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
unreleasedFlag("migrate_keyguard_status_bar_view")
+ /** Migrate clocks from keyguard status view to keyguard root view*/
+ // TODO(b/301502635): Tracking Bug.
+ @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
+ unreleasedFlag("migrate_clocks_to_blueprint")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -763,6 +768,9 @@
// TODO(b/289573946): Tracking Bug
@JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
+ // TODO(b/302087895): Tracking Bug
+ @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 06cf723..e8740a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -26,7 +26,6 @@
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -40,6 +39,8 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
import javax.inject.Inject
@@ -133,6 +134,7 @@
devicePostureRepository: DevicePostureRepository,
facePropertyRepository: FacePropertyRepository,
fingerprintPropertyRepository: FingerprintPropertyRepository,
+ mobileConnectionsRepository: MobileConnectionsRepository,
dumpManager: DumpManager,
) : BiometricSettingsRepository, Dumpable {
@@ -346,14 +348,15 @@
.and(isFingerprintBiometricAllowed)
.stateIn(scope, SharingStarted.Eagerly, false)
- override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
- get() = isFaceAuthenticationEnabled.and(isFaceEnrolled)
+ override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+ isFaceAuthenticationEnabled
+ .and(isFaceEnrolled)
+ .and(mobileConnectionsRepository.isAnySimSecure.isFalse())
- override val isFaceAuthCurrentlyAllowed: Flow<Boolean>
- get() =
- isFaceAuthEnrolledAndEnabled
- .and(isFaceBiometricsAllowed)
- .and(isFaceAuthSupportedInCurrentPosture)
+ override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
+ isFaceAuthEnrolledAndEnabled
+ .and(isFaceBiometricsAllowed)
+ .and(isFaceAuthSupportedInCurrentPosture)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -426,3 +429,5 @@
private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> = this.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index dd7eee9..abc30ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -25,6 +25,8 @@
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -383,6 +385,27 @@
falsingManager,
)
view.setOnTouchListener(onTouchListener)
+ view.setOnClickListener {
+ messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+ val amplitude =
+ view.context.resources
+ .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+ .toFloat()
+ val shakeAnimator =
+ ObjectAnimator.ofFloat(
+ view,
+ "translationX",
+ -amplitude / 2,
+ amplitude / 2,
+ )
+ shakeAnimator.duration =
+ KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+ shakeAnimator.interpolator =
+ CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.start()
+
+ vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ }
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 125e2da..f2d39da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -99,41 +99,7 @@
// When not using a stylus, lifting the finger/pointer will actually cancel
// the long-press gesture. Calling cancel after the quick affordance was
// already long-press activated is a no-op, so it's safe to call from here.
- cancel(
- onAnimationEnd =
- if (event.eventTime - event.downTime < longPressDurationMs) {
- Runnable {
- messageDisplayer.invoke(
- R.string.keyguard_affordance_press_too_short
- )
- val amplitude =
- view.context.resources
- .getDimensionPixelSize(
- R.dimen.keyguard_affordance_shake_amplitude
- )
- .toFloat()
- val shakeAnimator =
- ObjectAnimator.ofFloat(
- view,
- "translationX",
- -amplitude / 2,
- amplitude / 2,
- )
- shakeAnimator.duration =
- KeyguardBottomAreaVibrations.ShakeAnimationDuration
- .inWholeMilliseconds
- shakeAnimator.interpolator =
- CycleInterpolator(
- KeyguardBottomAreaVibrations.ShakeAnimationCycles
- )
- shakeAnimator.start()
-
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
- }
- } else {
- null
- }
- )
+ cancel()
}
false
}
@@ -168,10 +134,10 @@
view.setOnClickListener(null)
}
- fun cancel(onAnimationEnd: Runnable? = null) {
+ fun cancel() {
longPressAnimator?.cancel()
longPressAnimator = null
- view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
+ view.animate().scaleX(1f).scaleY(1f)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index eeb4ac3..aa76702 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -23,6 +23,8 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -216,6 +218,27 @@
falsingManager,
)
view.setOnTouchListener(onTouchListener)
+ view.setOnClickListener {
+ messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+ val amplitude =
+ view.context.resources
+ .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+ .toFloat()
+ val shakeAnimator =
+ ObjectAnimator.ofFloat(
+ view,
+ "translationX",
+ -amplitude / 2,
+ amplitude / 2,
+ )
+ shakeAnimator.duration =
+ KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+ shakeAnimator.interpolator =
+ CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.start()
+
+ vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ }
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 6c2ce7f..1943b34 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.log.LogcatEchoTrackerProd;
import com.android.systemui.log.table.TableLogBuffer;
import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.util.Compile;
import com.android.systemui.util.wakelock.WakeLockLog;
@@ -229,12 +230,12 @@
}
/**
- * Provides a logging buffer for logs related to {@link com.android.systemui.qs.QSFragment}'s
+ * Provides a logging buffer for logs related to {@link QSFragmentLegacy}'s
* disable flag adjustments.
*/
@Provides
@SysUISingleton
- @QSFragmentDisableLog
+ @QSDisableLog
public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */,
false /* systrace */);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
rename to packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
index 557a254..b3bceca 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
@@ -19,6 +19,7 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
+import com.android.systemui.qs.QSFragmentLegacy;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -27,10 +28,10 @@
/**
* A {@link LogBuffer} for disable flag adjustments made in
- * {@link com.android.systemui.qs.QSFragment}.
+ * {@link QSFragmentLegacy}.
*/
@Qualifier
@Documented
@Retention(RUNTIME)
-public @interface QSFragmentDisableLog {
+public @interface QSDisableLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index b5d3e91..88bc064 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.appselector
+package com.android.systemui.media
import android.app.ActivityOptions
import android.content.Intent
@@ -46,11 +46,13 @@
import com.android.internal.widget.RecyclerView
import com.android.internal.widget.RecyclerViewAccessibilityDelegate
import com.android.internal.widget.ResolverDrawerLayout
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
-import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
+import com.android.systemui.res.R
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
-import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.AsyncActivityLauncher
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
index 11d0be5..fbf9294 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.mediaprojection
+package com.android.systemui.media
import android.os.IBinder
import android.os.Parcel
import android.os.Parcelable
/**
- * Class that represents an area that should be captured. Currently it has only a launch cookie that
- * represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
+ * Class that represents an area that should be captured.
+ * Currently it has only a launch cookie that represents a task but
+ * we potentially could add more identifiers e.g. for a pair of tasks.
*/
-data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable {
+data class MediaProjectionCaptureTarget(
+ val launchCookie: IBinder?
+): Parcelable {
constructor(parcel: Parcel) : this(parcel.readStrongBinder())
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 2b56d0c..4de6278 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.permission;
+package com.android.systemui.media;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
@@ -22,8 +22,8 @@
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
-import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
import android.app.Activity;
@@ -51,13 +51,13 @@
import android.util.Log;
import android.view.Window;
+import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
-import com.android.systemui.res.R;
+import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
+import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
index f1cade7..9e616e2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.mediaprojection
+package com.android.systemui.media
import android.content.Context
import android.media.projection.IMediaProjection
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 72aea04..33d9cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -23,6 +23,8 @@
import androidx.lifecycle.DefaultLifecycleObserver
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -35,7 +37,6 @@
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
-import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 463c79c..eba1c25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -28,7 +28,7 @@
import com.android.app.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -86,8 +86,7 @@
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
- private final QuickStatusBarHeader mQuickStatusBarHeader;
- private final QS mQs;
+ private final View mQsRootView;
@Nullable
private PagedTileLayout mPagedLayout;
@@ -115,8 +114,6 @@
// Brightness slider opacity driver. Uses linear interpolator.
@Nullable
private TouchAnimator mBrightnessOpacityAnimator;
- // Animator for Footer actions in QQS
- private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
@Nullable
private HeightExpansionAnimator mQQSTileHeightAnimator;
@@ -144,22 +141,21 @@
private int[] mTmpLoc2 = new int[2];
@Inject
- public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+ public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
@Main Executor executor, TunerService tunerService,
QSExpansionPathInterpolator qsExpansionPathInterpolator) {
- mQs = qs;
+ mQsRootView = rootView;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
- mQuickStatusBarHeader = quickStatusBarHeader;
mHost = qsTileHost;
mExecutor = executor;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
- qs.getView().addOnLayoutChangeListener(this);
+ mQsRootView.addOnLayoutChangeListener(this);
if (mQsPanelController.isAttachedToWindow()) {
onViewAttachedToWindow(null);
}
@@ -314,8 +310,7 @@
break;
}
- final View tileIcon = tileView.getIcon().getIconView();
- View view = mQs.getView();
+ View view = mQsRootView;
// This case: less tiles to animate in small displays.
if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
@@ -480,7 +475,7 @@
.setStartDelay(QS_TILE_LABEL_FADE_OUT_START)
.setEndDelay(QS_TILE_LABEL_FADE_OUT_END);
SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
- View view = mQs.getView();
+ View view = mQsRootView;
List<String> specs = mPagedLayout.getSpecsForPage(page);
if (specs.isEmpty()) {
// specs should not be empty in a valid secondary page, as we scrolled to it.
@@ -577,7 +572,7 @@
// For (1), compute the distance via the vertical distance between QQS and QS tile
// layout top.
- View quickSettingsRootView = mQs.getView();
+ View quickSettingsRootView = mQsRootView;
View qsTileLayout = (View) mQsPanelController.getTileLayout();
View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
@@ -607,7 +602,7 @@
private int getRelativeTranslationY(View view1, View view2) {
int[] qsPosition = new int[2];
int[] qqsPosition = new int[2];
- View commonView = mQs.getView();
+ View commonView = mQsRootView;
getRelativePositionInt(qsPosition, view1, commonView);
getRelativePositionInt(qqsPosition, view2, commonView);
return qsPosition[1] - qqsPosition[1];
@@ -690,9 +685,6 @@
if (mBrightnessTranslationAnimator != null) {
mBrightnessTranslationAnimator.setPosition(position);
}
- if (mQQSFooterActionsAnimator != null) {
- mQQSFooterActionsAnimator.setPosition(position);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
rename to packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
index 6563e42..6f6f467 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
@@ -1,20 +1,22 @@
package com.android.systemui.qs
-import com.android.systemui.log.dagger.QSFragmentDisableLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.QSDisableLog
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
-/** A helper class for logging disable flag changes made in [QSFragment]. */
-class QSFragmentDisableFlagsLogger @Inject constructor(
- @QSFragmentDisableLog private val buffer: LogBuffer,
+/** A helper class for logging disable flag changes made in [QSImpl]. */
+class QSDisableFlagsLogger
+@Inject
+constructor(
+ @QSDisableLog private val buffer: LogBuffer,
private val disableFlagsLogger: DisableFlagsLogger
) {
/**
- * Logs a string representing the new state received by [QSFragment] and any modifications that
- * were made to the flags locally.
+ * Logs a string representing the new state received by [QSImpl] and any modifications that were
+ * made to the flags locally.
*
* @param new see [DisableFlagsLogger.getDisableFlagsString]
* @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString]
@@ -43,4 +45,4 @@
}
}
-private const val TAG = "QSFragmentDisableFlagsLog"
+private const val TAG = "QSDisableFlagsLog"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
new file mode 100644
index 0000000..8589ae9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -0,0 +1,369 @@
+/*
+ * 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.qs;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
+import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.LifecycleFragment;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+public class QSFragmentLegacy extends LifecycleFragment implements QS {
+
+ private final Provider<QSImpl> mQsImplProvider;
+
+ private final QSFragmentComponent.Factory mQsComponentFactory;
+
+ @Nullable
+ private QSImpl mQsImpl;
+
+ @Inject
+ public QSFragmentLegacy(
+ Provider<QSImpl> qsImplProvider,
+ QSFragmentComponent.Factory qsComponentFactory
+ ) {
+ mQsComponentFactory = qsComponentFactory;
+ mQsImplProvider = qsImplProvider;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ try {
+ Trace.beginSection("QSFragment#onCreateView");
+ inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+ R.style.Theme_SystemUI_QuickSettings));
+ return inflater.inflate(R.layout.qs_panel, container, false);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+ mQsImpl = mQsImplProvider.get();
+ mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
+ }
+
+ @Override
+ public void setScrollListener(ScrollListener listener) {
+ if (mQsImpl != null) {
+ mQsImpl.setScrollListener(listener);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (mQsImpl != null) {
+ mQsImpl.onCreate(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mQsImpl != null) {
+ mQsImpl.onDestroy();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mQsImpl != null) {
+ mQsImpl.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ public View getHeader() {
+ if (mQsImpl != null) {
+ return mQsImpl.getHeader();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void setHasNotifications(boolean hasNotifications) {
+ if (mQsImpl != null) {
+ mQsImpl.setHasNotifications(hasNotifications);
+ }
+ }
+
+ @Override
+ public void setPanelView(HeightListener panelView) {
+ if (mQsImpl != null) {
+ mQsImpl.setPanelView(panelView);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mQsImpl != null) {
+ mQsImpl.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom,
+ int cornerRadius, boolean visible, boolean fullWidth) {
+ if (mQsImpl != null) {
+ mQsImpl.setFancyClipping(leftInset, top, rightInset, bottom, cornerRadius, visible,
+ fullWidth);
+ }
+ }
+
+ @Override
+ public boolean isFullyCollapsed() {
+ if (mQsImpl != null) {
+ return mQsImpl.isFullyCollapsed();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void setCollapsedMediaVisibilityChangedListener(Consumer<Boolean> listener) {
+ if (mQsImpl != null) {
+ mQsImpl.setCollapsedMediaVisibilityChangedListener(listener);
+ }
+ }
+
+ @Override
+ public void setContainerController(QSContainerController controller) {
+ if (mQsImpl != null) {
+ mQsImpl.setContainerController(controller);
+ }
+ }
+
+ @Override
+ public boolean isCustomizing() {
+ if (mQsImpl != null) {
+ return mQsImpl.isCustomizing();
+ } else {
+ return false;
+ }
+ }
+
+ public QSPanelController getQSPanelController() {
+ if (mQsImpl != null) {
+ return mQsImpl.getQSPanelController();
+ } else {
+ return null;
+ }
+ }
+
+ public void setBrightnessMirrorController(
+ BrightnessMirrorController brightnessMirrorController) {
+ if (mQsImpl != null) {
+ mQsImpl.setBrightnessMirrorController(brightnessMirrorController);
+ }
+ }
+
+ @Override
+ public boolean isShowingDetail() {
+ if (mQsImpl != null) {
+ return mQsImpl.isShowingDetail();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void setHeaderClickable(boolean clickable) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeaderClickable(clickable);
+ }
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ if (mQsImpl != null) {
+ mQsImpl.setExpanded(expanded);
+ }
+ }
+
+ @Override
+ public void setOverscrolling(boolean stackScrollerOverscrolling) {
+ if (mQsImpl != null) {
+ mQsImpl.setOverscrolling(stackScrollerOverscrolling);
+ }
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (mQsImpl != null) {
+ mQsImpl.setListening(listening);
+ }
+ }
+
+ @Override
+ public void setQsVisible(boolean visible) {
+ if (mQsImpl != null) {
+ mQsImpl.setQsVisible(visible);
+ }
+ }
+
+ @Override
+ public void setHeaderListening(boolean listening) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeaderListening(listening);
+ }
+ }
+
+ @Override
+ public void notifyCustomizeChanged() {
+ if (mQsImpl != null) {
+ mQsImpl.notifyCustomizeChanged();
+ }
+ }
+
+ @Override
+ public void setInSplitShade(boolean inSplitShade) {
+ if (mQsImpl != null) {
+ mQsImpl.setInSplitShade(inSplitShade);
+ }
+ }
+
+ @Override
+ public void setTransitionToFullShadeProgress(
+ boolean isTransitioningToFullShade,
+ @FloatRange(from = 0.0, to = 1.0) float qsTransitionFraction,
+ @FloatRange(from = 0.0, to = 1.0) float qsSquishinessFraction) {
+ if (mQsImpl != null) {
+ mQsImpl.setTransitionToFullShadeProgress(isTransitioningToFullShade,
+ qsTransitionFraction, qsSquishinessFraction);
+ }
+ }
+
+ @Override
+ public void setOverScrollAmount(int overScrollAmount) {
+ if (mQsImpl != null) {
+ mQsImpl.setOverScrollAmount(overScrollAmount);
+ }
+ }
+
+ @Override
+ public int getHeightDiff() {
+ if (mQsImpl != null) {
+ return mQsImpl.getHeightDiff();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void setIsNotificationPanelFullWidth(boolean isFullWidth) {
+ if (mQsImpl != null) {
+ mQsImpl.setIsNotificationPanelFullWidth(isFullWidth);
+ }
+ }
+
+ @Override
+ public void setQsExpansion(float expansion, float panelExpansionFraction,
+ float proposedTranslation, float squishinessFraction) {
+ if (mQsImpl != null) {
+ mQsImpl.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+ }
+ }
+
+ @Override
+ public void animateHeaderSlidingOut() {
+ if (mQsImpl != null) {
+ mQsImpl.animateHeaderSlidingOut();
+ }
+ }
+
+ @Override
+ public void setCollapseExpandAction(Runnable action) {
+ if (mQsImpl != null) {
+ mQsImpl.setCollapseExpandAction(action);
+ }
+ }
+
+ @Override
+ public void closeDetail() {
+ if (mQsImpl != null) {
+ mQsImpl.closeDetail();
+ }
+ }
+
+ @Override
+ public void closeCustomizer() {
+ if (mQsImpl != null) {
+ mQsImpl.closeDetail();
+ }
+ }
+
+ /**
+ * The height this view wants to be. This is different from {@link View#getMeasuredHeight} such
+ * that during closing the detail panel, this already returns the smaller height.
+ */
+ @Override
+ public int getDesiredHeight() {
+ if (mQsImpl != null) {
+ return mQsImpl.getDesiredHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void setHeightOverride(int desiredHeight) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeightOverride(desiredHeight);
+ }
+ }
+
+ @Override
+ public int getQsMinExpansionHeight() {
+ if (mQsImpl != null) {
+ return mQsImpl.getQsMinExpansionHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void hideImmediately() {
+ if (mQsImpl != null) {
+ mQsImpl.hideImmediately();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
index 253560b..9fa6769 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -31,10 +31,13 @@
@Inject
constructor(
private val fragmentService: FragmentService,
- private val qsFragmentProvider: Provider<QSFragment>
+ private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
) : CoreStartable {
override fun start() {
- fragmentService.addFragmentInstantiationProvider(QSFragment::class.java, qsFragmentProvider)
+ fragmentService.addFragmentInstantiationProvider(
+ QSFragmentLegacy::class.java,
+ qsFragmentLegacyProvider
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
rename to packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index fd81e9a..a32a024 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -1,15 +1,17 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.qs;
@@ -20,21 +22,18 @@
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Trace;
import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import androidx.annotation.FloatRange;
@@ -47,7 +46,6 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
@@ -58,19 +56,20 @@
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.util.LifecycleFragment;
import com.android.systemui.util.Utils;
import java.io.PrintWriter;
@@ -80,8 +79,8 @@
import javax.inject.Inject;
import javax.inject.Named;
-public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks,
- StatusBarStateController.StateListener, Dumpable {
+public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateController.StateListener,
+ Dumpable {
private static final String TAG = "QS";
private static final boolean DEBUG = false;
private static final String EXTRA_EXPANDED = "expanded";
@@ -113,8 +112,7 @@
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final MediaHost mQsMediaHost;
private final MediaHost mQqsMediaHost;
- private final QSFragmentComponent.Factory mQsComponentFactory;
- private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
+ private final QSDisableFlagsLogger mQsDisableFlagsLogger;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
private final QSLogger mLogger;
@@ -167,14 +165,17 @@
private boolean mIsSmallScreen;
+ private CommandQueue mCommandQueue;
+
+ private View mRootView;
+
@Inject
- public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+ public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue,
@Named(QS_PANEL) MediaHost qsMediaHost,
@Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
KeyguardBypassController keyguardBypassController,
- QSFragmentComponent.Factory qsComponentFactory,
- QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
+ QSDisableFlagsLogger qsDisableFlagsLogger,
DumpManager dumpManager, QSLogger qsLogger,
FooterActionsController footerActionsController,
FooterActionsViewModel.Factory footerActionsViewModelFactory,
@@ -184,12 +185,11 @@
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
- mQsComponentFactory = qsComponentFactory;
- mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
+ mQsDisableFlagsLogger = qsDisableFlagsLogger;
mLogger = qsLogger;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
- commandQueue.observe(getLifecycle(), this);
+ mCommandQueue = commandQueue;
mBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
mDumpManager = dumpManager;
@@ -199,34 +199,23 @@
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
}
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- Bundle savedInstanceState) {
- try {
- Trace.beginSection("QSFragment#onCreateView");
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
- R.style.Theme_SystemUI_QuickSettings));
- return inflater.inflate(R.layout.qs_panel, container, false);
- } finally {
- Trace.endSection();
- }
- }
+ public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) {
+ mRootView = qsComponent.getRootView();
- @Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
- mQSPanelController = qsFragmentComponent.getQSPanelController();
- mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+ mCommandQueue.addCallback(this);
+
+ mQSPanelController = qsComponent.getQSPanelController();
+ mQuickQSPanelController = qsComponent.getQuickQSPanelController();
mQSPanelController.init();
mQuickQSPanelController.init();
- mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
- this);
- bindFooterActionsView(view);
+ mQSFooterActionsViewModel = mFooterActionsViewModelFactory
+ .create(mListeningAndVisibilityLifecycleOwner);
+ bindFooterActionsView(mRootView);
mFooterActionsController.init();
- mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
+ mQSPanelScrollView = mRootView.findViewById(R.id.expanded_qs_scroll_view);
mQSPanelScrollView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
updateQsBounds();
@@ -238,26 +227,26 @@
if (mScrollListener != null) {
mScrollListener.onQsPanelScrollChanged(scrollY);
}
- });
- mHeader = view.findViewById(R.id.header);
- mFooter = qsFragmentComponent.getQSFooter();
+ });
+ mHeader = mRootView.findViewById(R.id.header);
+ mFooter = qsComponent.getQSFooter();
- mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
+ mQSContainerImplController = qsComponent.getQSContainerImplController();
mQSContainerImplController.init();
mContainer = mQSContainerImplController.getView();
mDumpManager.registerDumpable(mContainer.getClass().getSimpleName(), mContainer);
- mQSAnimator = qsFragmentComponent.getQSAnimator();
- mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
+ mQSAnimator = qsComponent.getQSAnimator();
+ mQSSquishinessController = qsComponent.getQSSquishinessController();
- mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
+ mQSCustomizerController = qsComponent.getQSCustomizerController();
mQSCustomizerController.init();
mQSCustomizerController.setQs(this);
if (savedInstanceState != null) {
setQsVisible(savedInstanceState.getBoolean(EXTRA_VISIBLE));
setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
- setEditLocation(view);
+ setEditLocation(mRootView);
mQSCustomizerController.restoreInstanceState(savedInstanceState);
if (mQsExpanded) {
mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
@@ -265,7 +254,7 @@
}
mStatusBarStateController.addCallback(this);
onStateChanged(mStatusBarStateController.getState());
- view.addOnLayoutChangeListener(
+ mRootView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
@@ -327,15 +316,12 @@
mScrollListener = listener;
}
- @Override
public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
mDumpManager.registerDumpable(getClass().getSimpleName(), this);
}
- @Override
public void onDestroy() {
- super.onDestroy();
+ mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
if (mListening) {
setListening(false);
@@ -351,9 +337,7 @@
mListeningAndVisibilityLifecycleOwner.destroy();
}
- @Override
public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
outState.putBoolean(EXTRA_LISTENING, mListening);
outState.putBoolean(EXTRA_VISIBLE, mQsVisible);
@@ -394,9 +378,7 @@
mPanelView = panelView;
}
- @Override
public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
setEditLocation(getView());
if (newConfig.getLayoutDirection() != mLayoutDirection) {
mLayoutDirection = newConfig.getLayoutDirection();
@@ -452,9 +434,9 @@
int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- mQsFragmentDisableFlagsLogger.logDisableFlagChange(
- /* new= */ new DisableState(state1, state2BeforeAdjustment),
- /* newAfterLocalModification= */ new DisableState(state1, state2)
+ mQsDisableFlagsLogger.logDisableFlagChange(
+ /* new= */ new DisableFlagsLogger.DisableState(state1, state2BeforeAdjustment),
+ /* newAfterLocalModification= */ new DisableFlagsLogger.DisableState(state1, state2)
);
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
@@ -919,32 +901,6 @@
getView().setY(-getQsMinExpansionHeight());
}
- private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
- = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getView().getViewTreeObserver().removeOnPreDrawListener(this);
- getView().animate()
- .translationY(0f)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(mAnimateHeaderSlidingInListener)
- .start();
- return true;
- }
- };
-
- private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
- = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeaderAnimating = false;
- updateQsState();
- // Unset the listener, otherwise this may persist for another view property animation
- getView().animate().setListener(null);
- }
- };
-
@Override
public void onUpcomingStateChanged(int upcomingState) {
if (upcomingState == KEYGUARD) {
@@ -1030,6 +986,20 @@
return "GONE";
}
+ @Override
+ public View getView() {
+ return mRootView;
+ }
+
+ @Override
+ public Context getContext() {
+ return mRootView.getContext();
+ }
+
+ private Resources getResources() {
+ return getContext().getResources();
+ }
+
/**
* A {@link LifecycleOwner} whose state is driven by the current state of this fragment:
*
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 9359958..6bbdc54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -19,7 +19,7 @@
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
import android.view.MotionEvent;
import android.view.View;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ef81674..60c92c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,9 +43,6 @@
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
@@ -54,7 +50,8 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import javax.inject.Named;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
/**
* Controller for QSPanel views.
@@ -135,7 +132,7 @@
T view,
QSHost host,
QSCustomizerController qsCustomizerController,
- @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
+ boolean usingMediaPlayer,
MediaHost mediaHost,
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 099d19d8..f278dce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,14 +17,13 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -32,6 +31,7 @@
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.leak.RotationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7888f4c..a103566 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,11 +33,11 @@
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSUtils;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.LightBarController;
/**
@@ -135,8 +135,10 @@
setVisibility(View.VISIBLE);
long duration = mClipper.animateCircularClip(
mX, mY, true, new ExpandAnimatorListener(tileAdapter));
- mQsContainerController.setCustomizerAnimating(true);
- mQsContainerController.setCustomizerShowing(true, duration);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(true);
+ mQsContainerController.setCustomizerShowing(true, duration);
+ }
}
}
@@ -150,8 +152,10 @@
mClipper.showBackground();
isShown = true;
setCustomizing(true);
- mQsContainerController.setCustomizerAnimating(false);
- mQsContainerController.setCustomizerShowing(true);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerShowing(true);
+ }
}
}
@@ -169,8 +173,10 @@
} else {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(animate);
- mQsContainerController.setCustomizerShowing(false, duration);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(animate);
+ mQsContainerController.setCustomizerShowing(false, duration);
+ }
}
}
@@ -180,7 +186,9 @@
void setCustomizing(boolean customizing) {
mCustomizing = customizing;
- mQs.notifyCustomizeChanged();
+ if (mQs != null) {
+ mQs.notifyCustomizeChanged();
+ }
}
public boolean isCustomizing() {
@@ -208,15 +216,21 @@
setCustomizing(true);
}
mOpening = false;
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
mRecyclerView.setAdapter(mTileAdapter);
}
@Override
public void onAnimationCancel(Animator animation) {
mOpening = false;
- mQs.notifyCustomizeChanged();
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQs != null) {
+ mQs.notifyCustomizeChanged();
+ }
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
}
@@ -226,7 +240,9 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
@Override
@@ -234,7 +250,9 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index ce504b2..024e760 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -34,14 +34,14 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
-import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -199,7 +199,7 @@
}
/** */
- public void setQs(@Nullable QSFragment qsFragment) {
+ public void setQs(@Nullable QS qsFragment) {
mView.setQs(qsFragment);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
new file mode 100644
index 0000000..f3413b80
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
@@ -0,0 +1,40 @@
+package com.android.systemui.qs.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.QSAnimator
+import com.android.systemui.qs.QSContainerImplController
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSPanelController
+import com.android.systemui.qs.QSSquishinessController
+import com.android.systemui.qs.QuickQSPanelController
+import com.android.systemui.qs.customize.QSCustomizerController
+
+interface QSComponent {
+ /** Construct a [QSPanelController]. */
+ fun getQSPanelController(): QSPanelController
+
+ /** Construct a [QuickQSPanelController]. */
+ fun getQuickQSPanelController(): QuickQSPanelController
+
+ /** Construct a [QSAnimator]. */
+ fun getQSAnimator(): QSAnimator
+
+ /** Construct a [QSContainerImplController]. */
+ fun getQSContainerImplController(): QSContainerImplController
+
+ /** Construct a [QSFooter] */
+ fun getQSFooter(): QSFooter
+
+ /** Construct a [QSCustomizerController]. */
+ fun getQSCustomizerController(): QSCustomizerController
+
+ /** Construct a [QSSquishinessController]. */
+ fun getQSSquishinessController(): QSSquishinessController
+
+ /** Construct a [FooterActionsController]. */
+ fun getQSFooterActionController(): FooterActionsController
+
+ @RootView fun getRootView(): View
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
new file mode 100644
index 0000000..ba1aa62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.qs.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+@Subcomponent(modules = [QSFlexiglassModule::class])
+@QSScope
+interface QSFlexiglassComponent : QSComponent {
+
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(@RootView @BindsInstance rootView: View): QSFlexiglassComponent
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
new file mode 100644
index 0000000..36fac44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.qs.dagger
+
+import android.content.Context
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_COLLAPSED_LANDSCAPE_MEDIA
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_MEDIA_PLAYER
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module(includes = [QSScopeModule::class])
+interface QSFlexiglassModule {
+
+ @Module
+ companion object {
+
+ /** */
+ @Provides
+ @Named(QS_USING_MEDIA_PLAYER)
+ @JvmStatic
+ fun providesQSUsingMediaPlayer(context: Context?): Boolean {
+ return false
+ }
+
+ /** */
+ @Provides
+ @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+ @JvmStatic
+ fun providesQSUsingCollapsedLandscapeMedia(context: Context): Boolean {
+ return false
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 594f4f8..327e858 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,53 +16,21 @@
package com.android.systemui.qs.dagger;
-import com.android.systemui.qs.FooterActionsController;
-import com.android.systemui.qs.QSAnimator;
-import com.android.systemui.qs.QSContainerImplController;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.qs.QSSquishinessController;
-import com.android.systemui.qs.QuickQSPanelController;
-import com.android.systemui.qs.customize.QSCustomizerController;
+import com.android.systemui.qs.QSFragmentLegacy;
import dagger.BindsInstance;
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for {@link QSFragment}.
+ * Dagger Subcomponent for {@link QSFragmentLegacy}.
*/
@Subcomponent(modules = {QSFragmentModule.class})
@QSScope
-public interface QSFragmentComponent {
+public interface QSFragmentComponent extends QSComponent {
/** Factory for building a {@link QSFragmentComponent}. */
@Subcomponent.Factory
interface Factory {
- QSFragmentComponent create(@BindsInstance QSFragment qsFragment);
+ QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment);
}
-
- /** Construct a {@link QSPanelController}. */
- QSPanelController getQSPanelController();
-
- /** Construct a {@link QuickQSPanelController}. */
- QuickQSPanelController getQuickQSPanelController();
-
- /** Construct a {@link QSAnimator}. */
- QSAnimator getQSAnimator();
-
- /** Construct a {@link QSContainerImplController}. */
- QSContainerImplController getQSContainerImplController();
-
- /** Construct a {@link QSFooter} */
- QSFooter getQSFooter();
-
- /** Construct a {@link QSCustomizerController}. */
- QSCustomizerController getQSCustomizerController();
-
- /** Construct a {@link QSSquishinessController}. */
- QSSquishinessController getQSSquishinessController();
-
- /** Construct a {@link FooterActionsController}. */
- FooterActionsController getQSFooterActionController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index bcd9803..0c9c24d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -20,21 +20,11 @@
import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.content.Context;
-import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSContainerImpl;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFooterView;
-import com.android.systemui.qs.QSFooterViewController;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QuickQSPanel;
-import com.android.systemui.qs.QuickStatusBarHeader;
-import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.QSFragmentLegacy;
import javax.inject.Named;
@@ -45,93 +35,31 @@
/**
* Dagger Module for {@link QSFragmentComponent}.
*/
-@Module
-public interface QSFragmentModule {
- String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
- String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media";
+@Module(includes = {QSScopeModule.class})
+public interface QSFragmentModule {
- /**
- * Provide a context themed using the QS theme
- */
- @Provides
- @QSThemedContext
- static Context provideThemedContext(@RootView View view) {
- return view.getContext();
- }
-
- /** */
- @Provides
- @QSThemedContext
- static LayoutInflater provideThemedLayoutInflater(@QSThemedContext Context context) {
- return LayoutInflater.from(context);
- }
-
- /** */
@Provides
@RootView
- static View provideRootView(QSFragment qsFragment) {
+ static View provideRootView(QSFragmentLegacy qsFragment) {
return qsFragment.getView();
}
/** */
- @Provides
- static QSPanel provideQSPanel(@RootView View view) {
- return view.findViewById(R.id.quick_settings_panel);
- }
-
- /** */
- @Provides
- static QSContainerImpl providesQSContainerImpl(@RootView View view) {
- return view.findViewById(R.id.quick_settings_container);
- }
-
- /** */
@Binds
- QS bindQS(QSFragment qsFragment);
+ QS bindQS(QSFragmentLegacy qsFragment);
/** */
@Provides
- static QuickStatusBarHeader providesQuickStatusBarHeader(@RootView View view) {
- return view.findViewById(R.id.header);
- }
-
- /** */
- @Provides
- static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) {
- return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
- }
-
- /** */
- @Provides
- static QSFooterView providesQSFooterView(@RootView View view) {
- return view.findViewById(R.id.qs_footer);
- }
-
- /** */
- @Provides
- @QSScope
- static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
- qsFooterViewController.init();
- return qsFooterViewController;
- }
-
- /** */
- @Provides
- @QSScope
- static QSCustomizer providesQSCutomizer(@RootView View view) {
- return view.findViewById(R.id.qs_customize);
- }
-
- /** */
- @Provides
- @Named(QS_USING_MEDIA_PLAYER)
+ @Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
+
+
/** */
@Provides
- @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+ @Named(QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
return useCollapsedMediaInLandscape(context.getResources());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 03de3a0..92490e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -52,7 +52,7 @@
/**
* Module for QS dependencies
*/
-@Module(subcomponents = {QSFragmentComponent.class},
+@Module(subcomponents = {QSFragmentComponent.class, QSFlexiglassComponent.class},
includes = {
MediaModule.class,
QSExternalModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
new file mode 100644
index 0000000..e68ec4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.qs.dagger
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.QSContainerImpl
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSFooterView
+import com.android.systemui.qs.QSFooterViewController
+import com.android.systemui.qs.QSPanel
+import com.android.systemui.qs.QuickQSPanel
+import com.android.systemui.qs.QuickStatusBarHeader
+import com.android.systemui.qs.customize.QSCustomizer
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface QSScopeModule {
+ companion object {
+ const val QS_USING_MEDIA_PLAYER = "qs_using_media_player"
+ const val QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media"
+
+ @Provides
+ @QSThemedContext
+ @JvmStatic
+ fun provideThemedContext(@RootView view: View): Context {
+ return view.context
+ }
+
+ /** */
+ @Provides
+ @QSThemedContext
+ @JvmStatic
+ fun provideThemedLayoutInflater(@QSThemedContext context: Context): LayoutInflater {
+ return LayoutInflater.from(context)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun provideQSPanel(@RootView view: View): QSPanel {
+ return view.requireViewById<QSPanel>(R.id.quick_settings_panel)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQSContainerImpl(@RootView view: View): QSContainerImpl {
+ return view.requireViewById<QSContainerImpl>(R.id.quick_settings_container)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQuickStatusBarHeader(@RootView view: View): QuickStatusBarHeader {
+ return view.requireViewById<QuickStatusBarHeader>(R.id.header)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQuickQSPanel(quickStatusBarHeader: QuickStatusBarHeader): QuickQSPanel {
+ return quickStatusBarHeader.requireViewById<QuickQSPanel>(R.id.quick_qs_panel)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQSFooterView(@RootView view: View): QSFooterView {
+ return view.requireViewById<QSFooterView>(R.id.qs_footer)
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ @JvmStatic
+ fun providesQSFooter(qsFooterViewController: QSFooterViewController): QSFooter {
+ qsFooterViewController.init()
+ return qsFooterViewController
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ @JvmStatic
+ fun providesQSCutomizer(@RootView view: View): QSCustomizer {
+ return view.requireViewById<QSCustomizer>(R.id.qs_customize)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index b394a07..8b2c3de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -72,7 +72,7 @@
* Show the device monitoring dialog, expanded from [expandable] if it's not null.
*
* Important: [quickSettingsContext] *must* be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragment].
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy].
*/
fun showDeviceMonitoringDialog(quickSettingsContext: Context, expandable: Expandable?)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 769cb1f..64fa33c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,7 +23,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -34,6 +33,7 @@
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.res.R
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
import javax.inject.Named
@@ -43,7 +43,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -201,8 +200,8 @@
* will suspend indefinitely and will need to be cancelled to stop observing.
*
* Important: [quickSettingsContext] must be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragment], and the call to this function
- * must be cancelled when that fragment is destroyed.
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
+ * function must be cancelled when that fragment is destroyed.
*/
suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
footerActionsInteractor.deviceMonitoringDialogRequests.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index a4600fb..21aaa94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -20,8 +20,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostRepository
+import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -42,6 +46,11 @@
abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository
@Binds
+ abstract fun provideDefaultTilesRepository(
+ impl: DefaultTilesQSHostRepository
+ ): DefaultTilesRepository
+
+ @Binds
abstract fun bindCurrentTilesInteractor(
impl: CurrentTilesInteractorImpl
): CurrentTilesInteractor
@@ -56,6 +65,11 @@
@ClassKey(QSPipelineCoreStartable::class)
abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
+ @Binds
+ abstract fun provideQSSettingsRestoredRepository(
+ impl: QSSettingsRestoredBroadcastRepository
+ ): QSSettingsRestoredRepository
+
companion object {
/**
* Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
@@ -67,5 +81,12 @@
fun provideQSTileListLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create(QSPipelineLogger.TILE_LIST_TAG, maxSize = 700, systrace = false)
}
+
+ @Provides
+ @SysUISingleton
+ @QSRestoreLog
+ fun providesQSRestoreLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create(QSPipelineLogger.RESTORE_TAG, maxSize = 50, systrace = false)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
new file mode 100644
index 0000000..c964929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.qs.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** A [LogBuffer] for the QS pipeline to track restore of associated settings. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class QSRestoreLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
new file mode 100644
index 0000000..d962632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.qs.pipeline.data.model
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+/** Data restored from Quick Settings as part of Backup & Restore. */
+data class RestoreData(
+ val restoredTiles: List<TileSpec>,
+ val restoredAutoAddedTiles: Set<TileSpec>,
+ val userId: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
index 43a16b6..7998dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
@@ -16,28 +16,19 @@
package com.android.systemui.qs.pipeline.data.repository
-import android.database.ContentObserver
-import android.provider.Settings
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import android.util.SparseArray
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.SecureSettings
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
/** Repository to track what QS tiles have been auto-added */
interface AutoAddRepository {
/** Flow of tiles that have been auto-added */
- fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
+ suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
/** Mark a tile as having been auto-added */
suspend fun markTileAdded(userId: Int, spec: TileSpec)
@@ -47,89 +38,39 @@
* multiple times.
*/
suspend fun unmarkTileAdded(userId: Int, spec: TileSpec)
+
+ suspend fun reconcileRestore(restoreData: RestoreData)
}
/**
- * Implementation that tracks the auto-added tiles stored in [Settings.Secure.QS_AUTO_ADDED_TILES].
+ * Implementation of [AutoAddRepository] that delegates to an instance of [UserAutoAddRepository]
+ * for each user.
*/
@SysUISingleton
class AutoAddSettingRepository
@Inject
-constructor(
- private val secureSettings: SecureSettings,
- @Background private val bgDispatcher: CoroutineDispatcher,
-) : AutoAddRepository {
- override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
- return conflatedCallbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
+constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) :
+ AutoAddRepository {
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>()
- awaitClose { secureSettings.unregisterContentObserver(observer) }
- }
- .onStart { emit(Unit) }
- .map { secureSettings.getStringForUser(SETTING, userId) ?: "" }
- .distinctUntilChanged()
- .map {
- it.split(DELIMITER).map(TileSpec::create).filter { it !is TileSpec.Invalid }.toSet()
- }
- .flowOn(bgDispatcher)
+ override suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+ if (userId !in userAutoAddRepositories) {
+ val repository = userAutoAddRepositoryFactory.create(userId)
+ userAutoAddRepositories.put(userId, repository)
+ }
+ return userAutoAddRepositories.get(userId).autoAdded()
}
override suspend fun markTileAdded(userId: Int, spec: TileSpec) {
- if (spec is TileSpec.Invalid) {
- return
- }
- val added = load(userId).toMutableSet()
- if (added.add(spec)) {
- store(userId, added)
- }
+ userAutoAddRepositories.get(userId)?.markTileAdded(spec)
}
override suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) {
- if (spec is TileSpec.Invalid) {
- return
- }
- val added = load(userId).toMutableSet()
- if (added.remove(spec)) {
- store(userId, added)
- }
+ userAutoAddRepositories.get(userId)?.unmarkTileAdded(spec)
}
- private suspend fun store(userId: Int, tiles: Set<TileSpec>) {
- val toStore =
- tiles
- .filter { it !is TileSpec.Invalid }
- .joinToString(DELIMITER, transform = TileSpec::spec)
- withContext(bgDispatcher) {
- secureSettings.putStringForUser(
- SETTING,
- toStore,
- null,
- false,
- userId,
- true,
- )
- }
- }
-
- private suspend fun load(userId: Int): Set<TileSpec> {
- return withContext(bgDispatcher) {
- (secureSettings.getStringForUser(SETTING, userId) ?: "")
- .split(",")
- .map(TileSpec::create)
- .filter { it !is TileSpec.Invalid }
- .toSet()
- }
- }
-
- companion object {
- private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
- private const val DELIMITER = ","
+ override suspend fun reconcileRestore(restoreData: RestoreData) {
+ userAutoAddRepositories.get(restoreData.userId)?.reconcileRestore(restoreData)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
new file mode 100644
index 0000000..fe0a69b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+interface DefaultTilesRepository {
+ val defaultTiles: List<TileSpec>
+}
+
+@SysUISingleton
+class DefaultTilesQSHostRepository
+@Inject
+constructor(
+ @Main private val resources: Resources,
+) : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() =
+ QSHost.getDefaultSpecs(resources).map(TileSpec::create).filter {
+ it != TileSpec.Invalid
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
new file mode 100644
index 0000000..6cee116
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -0,0 +1,122 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
+interface QSSettingsRestoredRepository {
+ val restoreData: Flow<RestoreData>
+}
+
+@SysUISingleton
+class QSSettingsRestoredBroadcastRepository
+@Inject
+constructor(
+ broadcastDispatcher: BroadcastDispatcher,
+ logger: QSPipelineLogger,
+ @Application private val scope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : QSSettingsRestoredRepository {
+
+ override val restoreData =
+ flow {
+ val firstIntent = mutableMapOf<Int, Intent>()
+ broadcastDispatcher
+ .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
+ intent to receiver.sendingUserId
+ }
+ .filter { it.first.isCorrectSetting() }
+ .collect { (intent, user) ->
+ if (user !in firstIntent) {
+ firstIntent[user] = intent
+ } else {
+ val firstRestored = firstIntent.remove(user)!!
+ emit(processIntents(user, firstRestored, intent))
+ }
+ }
+ }
+ .catch { Log.e(TAG, "Error parsing broadcast", it) }
+ .flowOn(backgroundDispatcher)
+ .buffer(BUFFER_CAPACITY)
+ .shareIn(scope, SharingStarted.Eagerly)
+ .onEach(logger::logSettingsRestored)
+
+ private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
+ intent1.validateIntent()
+ intent2.validateIntent()
+ val setting1 = intent1.getStringExtra(Intent.EXTRA_SETTING_NAME)
+ val setting2 = intent2.getStringExtra(Intent.EXTRA_SETTING_NAME)
+ val (tiles, autoAdd) =
+ if (setting1 == TILES_SETTING && setting2 == AUTO_ADD_SETTING) {
+ intent1 to intent2
+ } else if (setting1 == AUTO_ADD_SETTING && setting2 == TILES_SETTING) {
+ intent2 to intent1
+ } else {
+ throw IllegalStateException("Wrong intents ($intent1, $intent2)")
+ }
+
+ return RestoreData(
+ (tiles.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
+ (autoAdd.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesSet(),
+ user,
+ )
+ }
+
+ private companion object {
+ private const val TAG = "QSSettingsRestoredBroadcastRepository"
+ // This capacity is the number of restore data that we will keep buffered in the shared
+ // flow. It is unlikely that at any given time there would be this many restores being
+ // processed by consumers, but just in case that a couple of users are restored at the
+ // same time and they need to be replayed for the consumers of the flow.
+ private const val BUFFER_CAPACITY = 10
+
+ private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
+ private const val TILES_SETTING = Settings.Secure.QS_TILES
+ private const val AUTO_ADD_SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+ private val requiredExtras =
+ listOf(
+ Intent.EXTRA_SETTING_NAME,
+ Intent.EXTRA_SETTING_PREVIOUS_VALUE,
+ Intent.EXTRA_SETTING_NEW_VALUE,
+ )
+
+ private fun Intent.isCorrectSetting(): Boolean {
+ val setting = getStringExtra(Intent.EXTRA_SETTING_NAME)
+ return setting == TILES_SETTING || setting == AUTO_ADD_SETTING
+ }
+
+ private fun Intent.validateIntent() {
+ requiredExtras.forEach { extra ->
+ if (!hasExtra(extra)) {
+ throw IllegalStateException("$this doesn't have $extra")
+ }
+ }
+ }
+
+ private fun String.toTilesList() = toTilesList(this)
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 47c99f2..00ea0b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -18,34 +18,19 @@
import android.annotation.UserIdInt
import android.content.res.Resources
-import android.database.ContentObserver
-import android.provider.Settings
import android.util.SparseArray
import com.android.systemui.res.R
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.retail.data.repository.RetailModeRepository
-import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.withContext
/** Repository that tracks the current tiles. */
interface TileSpecRepository {
@@ -55,7 +40,7 @@
*
* Tiles will never be [TileSpec.Invalid] in the list and it will never be empty.
*/
- fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
+ suspend fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
/**
* Adds a [tile] for a given [userId] at [position]. Using [POSITION_AT_END] will add the tile
@@ -81,6 +66,8 @@
*/
suspend fun setTiles(@UserIdInt userId: Int, tiles: List<TileSpec>)
+ suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -88,28 +75,23 @@
}
/**
- * Implementation of [TileSpecRepository] that persist the values of tiles in
- * [Settings.Secure.QS_TILES].
- *
- * All operations against [Settings] will be performed in a background thread.
+ * Implementation of [TileSpecRepository] that delegates to an instance of [UserTileSpecRepository]
+ * for each user.
*
* If the device is in retail mode, the tiles are fixed to the value of
* [R.string.quick_settings_tiles_retail_mode].
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class TileSpecSettingsRepository
@Inject
constructor(
- private val secureSettings: SecureSettings,
@Main private val resources: Resources,
private val logger: QSPipelineLogger,
private val retailModeRepository: RetailModeRepository,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val userTileSpecRepositoryFactory: UserTileSpecRepository.Factory,
) : TileSpecRepository {
- private val mutex = Mutex()
- private val tileSpecsPerUser = SparseArray<List<TileSpec>>()
-
private val retailModeTiles by lazy {
resources
.getString(R.string.quick_settings_tiles_retail_mode)
@@ -118,123 +100,59 @@
.filter { it !is TileSpec.Invalid }
}
- @OptIn(ExperimentalCoroutinesApi::class)
- override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ private val userTileRepositories = SparseArray<UserTileSpecRepository>()
+
+ override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ if (userId !in userTileRepositories) {
+ val userTileRepository = userTileSpecRepositoryFactory.create(userId)
+ userTileRepositories.put(userId, userTileRepository)
+ }
+ val realTiles = userTileRepositories.get(userId).tiles()
+
return retailModeRepository.retailMode.flatMapLatest { inRetailMode ->
if (inRetailMode) {
logger.logUsingRetailTiles()
flowOf(retailModeTiles)
} else {
- settingsTiles(userId)
+ realTiles
}
}
}
- private fun settingsTiles(userId: Int): Flow<List<TileSpec>> {
- return conflatedCallbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
-
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
-
- awaitClose { secureSettings.unregisterContentObserver(observer) }
- }
- .onStart { emit(Unit) }
- .map { loadTiles(userId) }
- .onEach { logger.logTilesChangedInSettings(it, userId) }
- .distinctUntilChanged()
- .map { parseTileSpecs(it, userId).also { storeTiles(userId, it) } }
- .distinctUntilChanged()
- .onEach { mutex.withLock { tileSpecsPerUser.put(userId, it) } }
- .flowOn(backgroundDispatcher)
- }
-
- override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) =
- mutex.withLock {
- if (tile == TileSpec.Invalid) {
- return
- }
- val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
- if (tile !in tilesList) {
- if (position < 0 || position >= tilesList.size) {
- tilesList.add(tile)
- } else {
- tilesList.add(position, tile)
- }
- storeTiles(userId, tilesList)
- tileSpecsPerUser.put(userId, tilesList)
- }
- }
-
- override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) =
- mutex.withLock {
- if (tiles.all { it == TileSpec.Invalid }) {
- return
- }
- val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
- if (tilesList.removeAll(tiles)) {
- storeTiles(userId, tilesList.toList())
- tileSpecsPerUser.put(userId, tilesList)
- }
- }
-
- override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) =
- mutex.withLock {
- val filtered = tiles.filter { it != TileSpec.Invalid }
- if (filtered.isNotEmpty()) {
- storeTiles(userId, filtered)
- tileSpecsPerUser.put(userId, tiles)
- }
- }
-
- private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+ override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) {
if (retailModeRepository.inRetailMode) {
- // No storing tiles when in retail mode
return
}
- val toStore =
- tiles
- .filter { it !is TileSpec.Invalid }
- .joinToString(DELIMITER, transform = TileSpec::spec)
- withContext(backgroundDispatcher) {
- secureSettings.putStringForUser(
- SETTING,
- toStore,
- null,
- false,
- forUser,
- true,
- )
+ if (tile is TileSpec.Invalid) {
+ return
}
+ userTileRepositories.get(userId)?.addTile(tile, position)
}
- private suspend fun loadTiles(userId: Int): String {
- return withContext(backgroundDispatcher) {
- secureSettings.getStringForUser(SETTING, userId) ?: ""
+ override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) {
+ if (retailModeRepository.inRetailMode) {
+ return
}
+ userTileRepositories.get(userId)?.removeTiles(tiles)
}
- private fun parseTileSpecs(tilesFromSettings: String, user: Int): List<TileSpec> {
- val fromSettings =
- tilesFromSettings.split(DELIMITER).map(TileSpec::create).filter {
- it != TileSpec.Invalid
- }
- return if (fromSettings.isNotEmpty()) {
- fromSettings.also { logger.logParsedTiles(it, false, user) }
- } else {
- QSHost.getDefaultSpecs(resources)
- .map(TileSpec::create)
- .filter { it != TileSpec.Invalid }
- .also { logger.logParsedTiles(it, true, user) }
+ override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) {
+ if (retailModeRepository.inRetailMode) {
+ return
}
+ userTileRepositories.get(userId)?.setTiles(tiles)
+ }
+
+ override suspend fun reconcileRestore(
+ restoreData: RestoreData,
+ currentAutoAdded: Set<TileSpec>
+ ) {
+ userTileRepositories
+ .get(restoreData.userId)
+ ?.reconcileRestore(restoreData, currentAutoAdded)
}
companion object {
- private const val SETTING = Settings.Secure.QS_TILES
- private const val DELIMITER = ","
+ private const val DELIMITER = TilesSettingConverter.DELIMITER
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
new file mode 100644
index 0000000..bb55fcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
@@ -0,0 +1,18 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+object TilesSettingConverter {
+
+ const val DELIMITER = ","
+
+ fun toTilesList(commaSeparatedTiles: String) =
+ commaSeparatedTiles.split(DELIMITER).map(TileSpec::create).filter { it != TileSpec.Invalid }
+
+ fun toTilesSet(commaSeparatedTiles: String) =
+ commaSeparatedTiles
+ .split(DELIMITER)
+ .map(TileSpec::create)
+ .filter { it != TileSpec.Invalid }
+ .toSet()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
new file mode 100644
index 0000000..d452241
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -0,0 +1,186 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [AutoAddRepository]. It provides a similar interface as
+ * [AutoAddRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserAutoAddRepository
+@AssistedInject
+constructor(
+ @Assisted private val userId: Int,
+ private val secureSettings: SecureSettings,
+ private val logger: QSPipelineLogger,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+) {
+
+ private val changeEvents = MutableSharedFlow<ChangeAction>(
+ extraBufferCapacity = CHANGES_BUFFER_SIZE
+ )
+
+ private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
+
+ suspend fun autoAdded(): StateFlow<Set<TileSpec>> {
+ if (!::_autoAdded.isInitialized) {
+ _autoAdded =
+ changeEvents
+ .scan(load().also { logger.logAutoAddTilesParsed(userId, it) }) {
+ current,
+ change ->
+ change.apply(current).also {
+ if (change is RestoreTiles) {
+ logger.logAutoAddTilesRestoredReconciled(userId, it)
+ }
+ }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(applicationScope)
+ .also { startFlowCollections(it) }
+ }
+ return _autoAdded
+ }
+
+ private fun startFlowCollections(autoAdded: StateFlow<Set<TileSpec>>) {
+ applicationScope.launch(bgDispatcher) {
+ launch { autoAdded.collect { store(it) } }
+ launch {
+ // As Settings is not the source of truth, once we started tracking tiles for a
+ // user, we don't want anyone to change the underlying setting. Therefore, if there
+ // are any changes that don't match with the source of truth (this class), we
+ // overwrite them with the current value.
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+ secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserver(observer) }
+ }
+ .map { load() }
+ .flowOn(bgDispatcher)
+ .collect { setting ->
+ val current = autoAdded.value
+ if (setting != current) {
+ store(current)
+ }
+ }
+ }
+ }
+ }
+
+ suspend fun markTileAdded(spec: TileSpec) {
+ if (spec is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(MarkTile(spec))
+ }
+
+ suspend fun unmarkTileAdded(spec: TileSpec) {
+ if (spec is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(UnmarkTile(spec))
+ }
+
+ private suspend fun store(tiles: Set<TileSpec>) {
+ val toStore =
+ tiles
+ .filter { it !is TileSpec.Invalid }
+ .joinToString(DELIMITER, transform = TileSpec::spec)
+ withContext(bgDispatcher) {
+ secureSettings.putStringForUser(
+ SETTING,
+ toStore,
+ null,
+ false,
+ userId,
+ true,
+ )
+ }
+ }
+
+ private suspend fun load(): Set<TileSpec> {
+ return withContext(bgDispatcher) {
+ (secureSettings.getStringForUser(SETTING, userId) ?: "").toTilesSet()
+ }
+ }
+
+ suspend fun reconcileRestore(restoreData: RestoreData) {
+ changeEvents.emit(RestoreTiles(restoreData))
+ }
+
+ private sealed interface ChangeAction {
+ fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec>
+ }
+
+ private data class MarkTile(
+ val tileSpec: TileSpec,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded.toMutableSet().apply { add(tileSpec) }
+ }
+ }
+
+ private data class UnmarkTile(
+ val tileSpec: TileSpec,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded.toMutableSet().apply { remove(tileSpec) }
+ }
+ }
+
+ private data class RestoreTiles(
+ val restoredData: RestoreData,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded + restoredData.restoredAutoAddedTiles
+ }
+ }
+
+ companion object {
+ private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+ private const val DELIMITER = ","
+ // We want a small buffer in case multiple changes come in at the same time (sometimes
+ // happens in first start. This should be enough to not lose changes.
+ private const val CHANGES_BUFFER_SIZE = 10
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(userId: Int): UserAutoAddRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
new file mode 100644
index 0000000..152fd0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -0,0 +1,252 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.annotation.UserIdInt
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [TileSpecRepository]. It provides a similar interface as
+ * [TileSpecRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserTileSpecRepository
+@AssistedInject
+constructor(
+ @Assisted private val userId: Int,
+ private val defaultTilesRepository: DefaultTilesRepository,
+ private val secureSettings: SecureSettings,
+ private val logger: QSPipelineLogger,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ private val defaultTiles: List<TileSpec>
+ get() = defaultTilesRepository.defaultTiles
+
+ private val changeEvents = MutableSharedFlow<ChangeAction>(
+ extraBufferCapacity = CHANGES_BUFFER_SIZE
+ )
+
+ private lateinit var _tiles: StateFlow<List<TileSpec>>
+
+ suspend fun tiles(): Flow<List<TileSpec>> {
+ if (!::_tiles.isInitialized) {
+ _tiles =
+ changeEvents
+ .scan(loadTilesFromSettingsAndParse(userId)) { current, change ->
+ change.apply(current).also {
+ if (current != it) {
+ if (change is RestoreTiles) {
+ logger.logTilesRestoredAndReconciled(current, it, userId)
+ } else {
+ logger.logProcessTileChange(change, it, userId)
+ }
+ }
+ }
+ }
+ .flowOn(backgroundDispatcher)
+ .stateIn(applicationScope)
+ .also { startFlowCollections(it) }
+ }
+ return _tiles
+ }
+
+ private fun startFlowCollections(tiles: StateFlow<List<TileSpec>>) {
+ applicationScope.launch(backgroundDispatcher) {
+ launch { tiles.collect { storeTiles(userId, it) } }
+ launch {
+ // As Settings is not the source of truth, once we started tracking tiles for a
+ // user, we don't want anyone to change the underlying setting. Therefore, if there
+ // are any changes that don't match with the source of truth (this class), we
+ // overwrite them with the current value.
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+ secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserver(observer) }
+ }
+ .map { loadTilesFromSettings(userId) }
+ .flowOn(backgroundDispatcher)
+ .collect { setting ->
+ val current = tiles.value
+ if (setting != current) {
+ storeTiles(userId, current)
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+ val toStore =
+ tiles
+ .filter { it !is TileSpec.Invalid }
+ .joinToString(DELIMITER, transform = TileSpec::spec)
+ withContext(backgroundDispatcher) {
+ secureSettings.putStringForUser(
+ SETTING,
+ toStore,
+ null,
+ false,
+ forUser,
+ true,
+ )
+ }
+ }
+
+ suspend fun addTile(tile: TileSpec, position: Int = TileSpecRepository.POSITION_AT_END) {
+ if (tile is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(AddTile(tile, position))
+ }
+
+ suspend fun removeTiles(tiles: Collection<TileSpec>) {
+ changeEvents.emit(RemoveTiles(tiles))
+ }
+
+ suspend fun setTiles(tiles: List<TileSpec>) {
+ changeEvents.emit(ChangeTiles(tiles))
+ }
+
+ private fun parseTileSpecs(fromSettings: List<TileSpec>, user: Int): List<TileSpec> {
+ return if (fromSettings.isNotEmpty()) {
+ fromSettings.also { logger.logParsedTiles(it, false, user) }
+ } else {
+ defaultTiles.also { logger.logParsedTiles(it, true, user) }
+ }
+ }
+
+ private suspend fun loadTilesFromSettingsAndParse(userId: Int): List<TileSpec> {
+ return parseTileSpecs(loadTilesFromSettings(userId), userId)
+ }
+
+ private suspend fun loadTilesFromSettings(userId: Int): List<TileSpec> {
+ return withContext(backgroundDispatcher) {
+ secureSettings.getStringForUser(SETTING, userId) ?: ""
+ }
+ .toTilesList()
+ }
+
+ suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>) {
+ changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
+ }
+
+ sealed interface ChangeAction {
+ fun apply(currentTiles: List<TileSpec>): List<TileSpec>
+ }
+
+ private data class AddTile(
+ val tileSpec: TileSpec,
+ val position: Int = TileSpecRepository.POSITION_AT_END
+ ) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ val tilesList = currentTiles.toMutableList()
+ if (tileSpec !in tilesList) {
+ if (position < 0 || position >= tilesList.size) {
+ tilesList.add(tileSpec)
+ } else {
+ tilesList.add(position, tileSpec)
+ }
+ }
+ return tilesList
+ }
+ }
+
+ private data class RemoveTiles(val tileSpecs: Collection<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return currentTiles.toMutableList().apply { removeAll(tileSpecs) }
+ }
+ }
+
+ private data class ChangeTiles(
+ val newTiles: List<TileSpec>,
+ ) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ val new = newTiles.filter { it !is TileSpec.Invalid }
+ return if (new.isNotEmpty()) new else currentTiles
+ }
+ }
+
+ private data class RestoreTiles(
+ val restoreData: RestoreData,
+ val currentAutoAdded: Set<TileSpec>,
+ ) : ChangeAction {
+
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return reconcileTiles(currentTiles, currentAutoAdded, restoreData)
+ }
+ }
+
+ companion object {
+ private const val SETTING = Settings.Secure.QS_TILES
+ private const val DELIMITER = TilesSettingConverter.DELIMITER
+ // We want a small buffer in case multiple changes come in at the same time (sometimes
+ // happens in first start. This should be enough to not lose changes.
+ private const val CHANGES_BUFFER_SIZE = 10
+
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+ fun reconcileTiles(
+ currentTiles: List<TileSpec>,
+ currentAutoAdded: Set<TileSpec>,
+ restoreData: RestoreData
+ ): List<TileSpec> {
+ val toRestore = restoreData.restoredTiles.toMutableList()
+ val freshlyAutoAdded =
+ currentAutoAdded.filterNot { it in restoreData.restoredAutoAddedTiles }
+ freshlyAutoAdded
+ .filter { it in currentTiles && it !in restoreData.restoredTiles }
+ .map { it to currentTiles.indexOf(it) }
+ .sortedBy { it.second }
+ .forEachIndexed { iteration, (tile, position) ->
+ val insertAt = position + iteration
+ if (insertAt > toRestore.size) {
+ toRestore.add(tile)
+ } else {
+ toRestore.add(insertAt, tile)
+ }
+ }
+
+ return toRestore
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ userId: Int,
+ ): UserTileSpecRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 5a5e47a..00c2358 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.Intent
import android.os.UserHandle
+import android.util.Log
import com.android.systemui.Dumpable
import com.android.systemui.ProtoDumpable
import com.android.systemui.dagger.SysUISingleton
@@ -268,6 +269,7 @@
// repository
launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
}
+ Log.d("Fabian", "Finished resolving tiles")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
new file mode 100644
index 0000000..9844903
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -0,0 +1,53 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
+
+/**
+ * Interactor in charge of triggering reconciliation after QS Secure Settings are restored. For a
+ * given user, it will trigger the reconciliations in the correct order to prevent race conditions.
+ *
+ * Currently, the order is:
+ * 1. TileSpecRepository, with the restored data and the current (before restore) auto add tiles
+ * 2. AutoAddRepository
+ *
+ * [start] needs to be called to trigger the collection of [QSSettingsRestoredRepository].
+ */
+@SysUISingleton
+class RestoreReconciliationInteractor
+@Inject
+constructor(
+ private val tileSpecRepository: TileSpecRepository,
+ private val autoAddRepository: AutoAddRepository,
+ private val qsSettingsRestoredRepository: QSSettingsRestoredRepository,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ fun start() {
+ applicationScope.launch(backgroundDispatcher) {
+ qsSettingsRestoredRepository.restoreData.flatMapConcat { data ->
+ autoAddRepository.autoAddedTiles(data.userId)
+ .take(1)
+ .map { tiles -> data to tiles }
+ }.collect { (restoreData, autoAdded) ->
+ tileSpecRepository.reconcileRestore(restoreData, autoAdded)
+ autoAddRepository.reconcileRestore(restoreData)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index 0743ba0..1539f05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractor
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import javax.inject.Inject
@@ -30,11 +31,13 @@
private val currentTilesInteractor: CurrentTilesInteractor,
private val autoAddInteractor: AutoAddInteractor,
private val featureFlags: QSPipelineFlagsRepository,
+ private val restoreReconciliationInteractor: RestoreReconciliationInteractor,
) : CoreStartable {
override fun start() {
if (featureFlags.pipelineAutoAddEnabled) {
autoAddInteractor.init(currentTilesInteractor)
+ restoreReconciliationInteractor.start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index 573cb715..bca86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -16,11 +16,13 @@
package com.android.systemui.qs.pipeline.shared.logging
-import android.annotation.UserIdInt
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
+import com.android.systemui.qs.pipeline.dagger.QSRestoreLog
import com.android.systemui.qs.pipeline.dagger.QSTileListLog
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.UserTileSpecRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
@@ -34,11 +36,13 @@
constructor(
@QSTileListLog private val tileListLogBuffer: LogBuffer,
@QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer,
+ @QSRestoreLog private val restoreLogBuffer: LogBuffer,
) {
companion object {
const val TILE_LIST_TAG = "QSTileListLog"
const val AUTO_ADD_TAG = "QSAutoAddableLog"
+ const val RESTORE_TAG = "QSRestoreLog"
}
/**
@@ -60,20 +64,37 @@
)
}
- /**
- * Logs when the tiles change in Settings.
- *
- * This could be caused by SystemUI, or restore.
- */
- fun logTilesChangedInSettings(newTiles: String, @UserIdInt user: Int) {
+ fun logTilesRestoredAndReconciled(
+ currentTiles: List<TileSpec>,
+ reconciledTiles: List<TileSpec>,
+ user: Int,
+ ) {
tileListLogBuffer.log(
TILE_LIST_TAG,
- LogLevel.VERBOSE,
+ LogLevel.DEBUG,
{
- str1 = newTiles
+ str1 = currentTiles.toString()
+ str2 = reconciledTiles.toString()
int1 = user
},
- { "Tiles changed in settings for user $int1: $str1" }
+ { "Tiles restored and reconciled for user: $int1\nWas: $str1\nSet to: $str2" }
+ )
+ }
+
+ fun logProcessTileChange(
+ action: UserTileSpecRepository.ChangeAction,
+ newList: List<TileSpec>,
+ userId: Int,
+ ) {
+ tileListLogBuffer.log(
+ TILE_LIST_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = action.toString()
+ str2 = newList.toString()
+ int1 = userId
+ },
+ { "Processing $str1 for user $int1\nNew list: $str2" }
)
}
@@ -139,6 +160,30 @@
)
}
+ fun logAutoAddTilesParsed(userId: Int, tiles: Set<TileSpec>) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = tiles.toString()
+ int1 = userId
+ },
+ { "Auto add tiles parsed for user $int1: $str1" }
+ )
+ }
+
+ fun logAutoAddTilesRestoredReconciled(userId: Int, tiles: Set<TileSpec>) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = tiles.toString()
+ int1 = userId
+ },
+ { "Auto-add tiles reconciled for user $int1: $str1" }
+ )
+ }
+
fun logTileAutoAdded(userId: Int, spec: TileSpec, position: Int) {
tileAutoAddLogBuffer.log(
AUTO_ADD_TAG,
@@ -164,6 +209,23 @@
)
}
+ fun logSettingsRestored(restoreData: RestoreData) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = restoreData.userId
+ str1 = restoreData.restoredTiles.toString()
+ str2 = restoreData.restoredAutoAddedTiles.toString()
+ },
+ {
+ "Restored settings data for user $int1\n" +
+ "\tRestored tiles: $str1\n" +
+ "\tRestored auto added tiles: $str2"
+ }
+ )
+ }
+
/** Reasons for destroying an existing tile. */
enum class TileDestroyedReason(val readable: String) {
TILE_REMOVED("Tile removed from current set"),
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 8b437c3..64006fe 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
import android.content.Context
import android.os.Bundle
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index 2f10ad3..47e28d8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
import android.content.Context
import android.media.projection.MediaProjectionConfig
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 3501b6b..7cdb655 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -41,10 +41,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
-import com.android.systemui.res.R;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 3aab3bf..b80a01212 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -51,7 +51,7 @@
import android.view.Surface;
import android.view.WindowManager;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
import java.io.Closeable;
import java.io.File;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f2e94e9..f0ce8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,7 @@
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
+import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
@@ -41,8 +41,8 @@
import androidx.annotation.Nullable;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.res.R;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index a1d5d98..b5b7043 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -32,14 +32,10 @@
import android.widget.Spinner
import android.widget.Switch
import androidx.annotation.LayoutRes
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
-import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
-import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
-import com.android.systemui.mediaprojection.permission.SINGLE_APP
-import com.android.systemui.mediaprojection.permission.ScreenShareOption
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserContextProvider
/** Dialog to select screen recording options */
@@ -62,7 +58,6 @@
private lateinit var tapsView: View
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
@@ -182,7 +177,6 @@
)
private const val DELAY_MS: Long = 3000
private const val INTERVAL_MS: Long = 1000
-
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
index 37e8d9f..ebf0dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
import androidx.annotation.IntDef
import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index f08eb14..4b3bd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -53,9 +53,6 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.media.AudioAttributes;
-import android.media.AudioSystem;
-import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
@@ -86,8 +83,6 @@
import android.window.OnBackInvokedDispatcher;
import android.window.WindowContext;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
-
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
@@ -108,7 +103,6 @@
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
-import java.io.File;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@@ -116,11 +110,11 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import javax.inject.Provider;
+
/**
* Controls the state and flow for screenshots.
@@ -274,7 +268,8 @@
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final AccessibilityManager mAccessibilityManager;
- private final ListenableFuture<MediaPlayer> mCameraSound;
+ @Nullable
+ private final ScreenshotSoundController mScreenshotSoundController;
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
@@ -339,6 +334,7 @@
UserManager userManager,
AssistContentRequester assistContentRequester,
MessageContainerController messageContainerController,
+ Provider<ScreenshotSoundController> screenshotSoundController,
@Assisted int displayId
) {
mScreenshotSmartActions = screenshotSmartActions;
@@ -387,8 +383,12 @@
mConfigChanges.applyNewConfig(context.getResources());
reloadAssets();
- // Setup the Camera shutter sound
- mCameraSound = loadCameraSound();
+ // Sound is only reproduced from the controller of the default display.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mScreenshotSoundController = screenshotSoundController.get();
+ } else {
+ mScreenshotSoundController = null;
+ }
mCopyBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -573,17 +573,8 @@
}
private void releaseMediaPlayer() {
- // Note that this may block if the sound is still being loaded (very unlikely) but we can't
- // reliably release in the background because the service is being destroyed.
- try {
- MediaPlayer player = mCameraSound.get(1, TimeUnit.SECONDS);
- if (player != null) {
- player.release();
- }
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- mCameraSound.cancel(true);
- Log.w(TAG, "Error releasing shutter sound", e);
- }
+ if (mScreenshotSoundController == null) return;
+ mScreenshotSoundController.releaseScreenshotSound();
}
private void respondToKeyDismissal() {
@@ -889,39 +880,10 @@
}
}
- private ListenableFuture<MediaPlayer> loadCameraSound() {
- // The media player creation is slow and needs on the background thread.
- return CallbackToFutureAdapter.getFuture((completer) -> {
- mBgExecutor.execute(() -> {
- try {
- MediaPlayer player = MediaPlayer.create(mContext,
- Uri.fromFile(new File(mContext.getResources().getString(
- com.android.internal.R.string.config_cameraShutterSound))),
- null,
- new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .build(), AudioSystem.newAudioSessionId());
- completer.set(player);
- } catch (IllegalStateException e) {
- Log.w(TAG, "Screenshot sound initialization failed", e);
- completer.set(null);
- }
- });
- return "ScreenshotController#loadCameraSound";
- });
- }
-
- private void playCameraSound() {
- mCameraSound.addListener(() -> {
- try {
- MediaPlayer player = mCameraSound.get();
- if (player != null) {
- player.start();
- }
- } catch (InterruptedException | ExecutionException e) {
- }
- }, mBgExecutor);
+ private void playCameraSoundIfNeeded() {
+ if (mScreenshotSoundController == null) return;
+ // the controller is not-null only on the default display controller
+ mScreenshotSoundController.playCameraSound();
}
/**
@@ -930,7 +892,7 @@
*/
private void saveScreenshotAndToast(UserHandle owner, Consumer<Uri> finisher) {
// Play the shutter sound to notify that we've taken a screenshot
- playCameraSound();
+ playCameraSoundIfNeeded();
saveScreenshotInWorkerThread(
owner,
@@ -974,7 +936,7 @@
}
// Play the shutter sound to notify that we've taken a screenshot
- playCameraSound();
+ playCameraSoundIfNeeded();
if (DEBUG_ANIM) {
Log.d(TAG, "starting post-screenshot animation");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
new file mode 100644
index 0000000..cd0cab5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.screenshot
+
+import android.media.MediaPlayer
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.TraceUtils.Companion.tracedAsync
+import com.google.errorprone.annotations.CanIgnoreReturnValue
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.withTimeout
+
+/** Controls sound reproduction after a screenshot is taken. */
+interface ScreenshotSoundController {
+ /** Reproduces the camera sound. */
+ @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit>
+
+ /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */
+ @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit>
+}
+
+class ScreenshotSoundControllerImpl
+@Inject
+constructor(
+ private val soundProvider: ScreenshotSoundProvider,
+ @Application private val coroutineScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher
+) : ScreenshotSoundController {
+
+ val player: Deferred<MediaPlayer?> =
+ coroutineScope.tracedAsync("loadCameraSound", bgDispatcher) {
+ try {
+ soundProvider.getScreenshotSound()
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "Screenshot sound initialization failed", e)
+ null
+ }
+ }
+
+ override fun playCameraSound(): Deferred<Unit> {
+ return coroutineScope.tracedAsync("playCameraSound", bgDispatcher) {
+ player.await()?.start()
+ }
+ }
+ override fun releaseScreenshotSound(): Deferred<Unit> {
+ return coroutineScope.tracedAsync("releaseScreenshotSound", bgDispatcher) {
+ try {
+ withTimeout(1.seconds) { player.await()?.release() }
+ } catch (e: TimeoutCancellationException) {
+ player.cancel()
+ Log.w(TAG, "Error releasing shutter sound", e)
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "ScreenshotSoundControllerImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
new file mode 100644
index 0000000..73151d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.screenshot
+
+import android.content.Context
+import android.media.AudioAttributes
+import android.media.AudioSystem
+import android.media.MediaPlayer
+import android.net.Uri
+import com.android.internal.R
+import com.android.systemui.dagger.SysUISingleton
+import java.io.File
+import javax.inject.Inject
+
+/** Provides a [MediaPlayer] that reproduces the screenshot sound. */
+interface ScreenshotSoundProvider {
+
+ /**
+ * Creates a new [MediaPlayer] that reproduces the screenshot sound. This should be called from
+ * a background thread, as it might take time.
+ */
+ fun getScreenshotSound(): MediaPlayer
+}
+
+@SysUISingleton
+class ScreenshotSoundProviderImpl
+@Inject
+constructor(
+ private val context: Context,
+) : ScreenshotSoundProvider {
+ override fun getScreenshotSound(): MediaPlayer {
+ return MediaPlayer.create(
+ context,
+ Uri.fromFile(File(context.resources.getString(R.string.config_cameraShutterSound))),
+ /* holder = */ null,
+ AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build(),
+ AudioSystem.newAudioSessionId()
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 7d17d4c..3797b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -25,6 +25,10 @@
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotProxyService;
import com.android.systemui.screenshot.ScreenshotRequestProcessor;
+import com.android.systemui.screenshot.ScreenshotSoundController;
+import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
+import com.android.systemui.screenshot.ScreenshotSoundProvider;
+import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
@@ -69,4 +73,12 @@
@Binds
abstract ScreenshotRequestProcessor bindScreenshotRequestProcessor(
RequestProcessor requestProcessor);
+
+ @Binds
+ abstract ScreenshotSoundProvider bindScreenshotSoundProvider(
+ ScreenshotSoundProviderImpl screenshotSoundProviderImpl);
+
+ @Binds
+ abstract ScreenshotSoundController bindScreenshotSoundController(
+ ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index e8be40e..9b74ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -791,7 +791,8 @@
/** update Qs height state */
public void setExpansionHeight(float height) {
// TODO(b/277909752): remove below log when bug is fixed
- if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0) {
+ if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0
+ && mBarState == SHADE) {
Log.wtf(TAG,
"setting QS height to 0 in split shade while shade is open(ing). "
+ "Value of mExpandImmediate = " + mExpandImmediate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 670fb12..93bb435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -172,6 +172,7 @@
private static final int MSG_LOCK_TASK_MODE_CHANGED = 75 << MSG_SHIFT;
private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
+ private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -301,6 +302,8 @@
default void addQsTile(ComponentName tile) { }
default void remQsTile(ComponentName tile) { }
+
+ default void setQsTiles(String[] tiles) {}
default void clickTile(ComponentName tile) { }
default void handleSystemKey(KeyEvent arg1) { }
@@ -903,6 +906,13 @@
}
@Override
+ public void setQsTiles(String[] tiles) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_QS_TILES, tiles).sendToTarget();
+ }
+ }
+
+ @Override
public void clickQsTile(ComponentName tile) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_CLICK_QS_TILE, tile).sendToTarget();
@@ -1533,6 +1543,11 @@
mCallbacks.get(i).remQsTile((ComponentName) msg.obj);
}
break;
+ case MSG_SET_QS_TILES:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setQsTiles((String[]) msg.obj);
+ }
+ break;
case MSG_CLICK_QS_TILE:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).clickTile((ComponentName) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index e632214..37a4ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -104,8 +104,9 @@
// Record the HISTORY_SIZE most recent states
private int mHistoryIndex = 0;
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
- // This is used by InteractionJankMonitor to get callback from HWUI.
+ // These views are used by InteractionJankMonitor to get callback from HWUI.
private View mView;
+ private KeyguardClockSwitch mClockSwitchView;
/**
* If any of the system bars is hidden.
@@ -334,6 +335,7 @@
if ((mView == null || !mView.isAttachedToWindow())
&& (view != null && view.isAttachedToWindow())) {
mView = view;
+ mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -416,21 +418,12 @@
/** Returns the id of the currently rendering clock */
public String getClockId() {
- if (mView == null) {
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
-
- View clockSwitch = mView.findViewById(R.id.keyguard_clock_container);
- if (clockSwitch == null) {
+ if (mClockSwitchView == null) {
Log.e(TAG, "Clock container was missing");
return KeyguardClockSwitch.MISSING_CLOCK_ID;
}
- if (!(clockSwitch instanceof KeyguardClockSwitch)) {
- Log.e(TAG, "Clock container was incorrect type: " + clockSwitch);
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
- return ((KeyguardClockSwitch) clockSwitch).getClockId();
+ return mClockSwitchView.getClockId();
}
private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 88994b9..6ec9dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -100,7 +100,11 @@
/**
* The notification is coming from a suspended packages, so FSI is suppressed.
*/
- NO_FSI_SUSPENDED(false);
+ NO_FSI_SUSPENDED(false),
+ /**
+ * The device is not provisioned, launch FSI.
+ */
+ FSI_NOT_PROVISIONED(true);
public final boolean shouldLaunch;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 0c43da0..3819843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +76,7 @@
private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private final UiEventLogger mUiEventLogger;
private final UserTracker mUserTracker;
+ private final DeviceProvisionedController mDeviceProvisionedController;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -121,7 +123,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
mContentResolver = contentResolver;
mPowerManager = powerManager;
mBatteryController = batteryController;
@@ -163,6 +166,7 @@
headsUpObserver);
}
headsUpObserver.onChange(true); // set up
+ mDeviceProvisionedController = deviceProvisionedController;
}
@Override
@@ -334,6 +338,12 @@
}
}
+ // The device is not provisioned, launch FSI.
+ if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+ return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_NOT_PROVISIONED,
+ suppressedByDND);
+ }
+
// Detect the case determined by b/231322873 to launch FSI while device is in use,
// as blocked by the correct implementation, and report the event.
return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4114eb2..a8d59d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -71,15 +71,15 @@
private var appUid: Int? = null
private var packageName: String? = null
private var appName: String? = null
+ private var channel: NotificationChannel? = null
private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
// Caller should set this if they care about when we dismiss
var onFinishListener: OnChannelEditorDialogFinishedListener? = null
- @VisibleForTesting
- internal val paddedChannels = mutableListOf<NotificationChannel>()
// Channels handed to us from NotificationInfo
- private val providedChannels = mutableListOf<NotificationChannel>()
+ @VisibleForTesting
+ internal val channelList = mutableListOf<NotificationChannel>()
// Map from NotificationChannel to importance
private val edits = mutableMapOf<NotificationChannel, Int>()
@@ -93,14 +93,14 @@
private val channelGroupList = mutableListOf<NotificationChannelGroup>()
/**
- * Give the controller all of the information it needs to present the dialog
+ * Give the controller all the information it needs to present the dialog
* for a given app. Does a bunch of querying of NoMan, but won't present anything yet
*/
fun prepareDialogForApp(
appName: String,
packageName: String,
uid: Int,
- channels: Set<NotificationChannel>,
+ channel: NotificationChannel,
appIcon: Drawable,
onSettingsClickListener: NotificationInfo.OnSettingsClickListener?
) {
@@ -110,6 +110,7 @@
this.appIcon = appIcon
this.appNotificationsEnabled = checkAreAppNotificationsOn()
this.onSettingsClickListener = onSettingsClickListener
+ this.channel = channel
// These will always start out the same
appNotificationsCurrentlyEnabled = appNotificationsEnabled
@@ -117,9 +118,7 @@
channelGroupList.clear()
channelGroupList.addAll(fetchNotificationChannelGroups())
buildGroupNameLookup()
- providedChannels.clear()
- providedChannels.addAll(channels)
- padToFourChannels(channels)
+ populateChannelList()
initDialog()
prepared = true
@@ -133,36 +132,26 @@
}
}
- private fun padToFourChannels(channels: Set<NotificationChannel>) {
- paddedChannels.clear()
- // First, add all of the given channels
- paddedChannels.addAll(channels.asSequence().take(4))
-
- // Then pad to 4 if we haven't been given that many
- paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence())
- .filterNot { paddedChannels.contains(it) }
- .distinct()
- .take(4 - paddedChannels.size))
-
- // If we only got one channel and it has the default miscellaneous tag, then we actually
- // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the
- // channel
- if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) {
- paddedChannels.clear()
+ private fun populateChannelList() {
+ channelList.clear()
+ if (DEFAULT_CHANNEL_ID != channel!!.id) {
+ channelList.add(0, channel!!)
+ channelList.addAll(getDisplayableChannels(channelGroupList.asSequence())
+ .filterNot { it.id == channel!!.id }
+ .distinct())
}
}
private fun getDisplayableChannels(
groupList: Sequence<NotificationChannelGroup>
): Sequence<NotificationChannel> {
-
- // TODO (b/194833441): remove channel level settings when we move to a permission
val channels = groupList
.flatMap { group ->
- group.channels.asSequence().filterNot { channel ->
- channel.importance == IMPORTANCE_NONE ||
+ group.channels.asSequence()
+ .sortedWith(compareBy {group.name?.toString() ?: group.id})
+ .filterNot { channel ->
channel.isImportanceLockedByCriticalDeviceFunction
- }
+ }
}
// TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not)
@@ -196,8 +185,7 @@
appNotificationsCurrentlyEnabled = null
edits.clear()
- paddedChannels.clear()
- providedChannels.clear()
+ channelList.clear()
groupNameLookup.clear()
}
@@ -231,7 +219,7 @@
@Suppress("unchecked_cast")
private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
return try {
- noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
+ noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!)
.list as? List<NotificationChannelGroup> ?: listOf()
} catch (e: Exception) {
Log.e(TAG, "Error fetching channel groups", e)
@@ -280,7 +268,6 @@
@VisibleForTesting
fun launchSettings(sender: View) {
- val channel = if (providedChannels.size == 1) providedChannels[0] else null
onSettingsClickListener?.onClick(sender, channel, appUid!!)
}
@@ -301,14 +288,12 @@
controller = this@ChannelEditorDialogController
appIcon = this@ChannelEditorDialogController.appIcon
appName = this@ChannelEditorDialogController.appName
- channels = paddedChannels
+ channels = channelList
}
setOnShowListener {
- // play a highlight animation for the given channels
- for (channel in providedChannels) {
- listView?.highlightChannel(channel)
- }
+ // play a highlight animation for the given channel
+ listView?.highlightChannel(channel!!)
}
findViewById<TextView>(R.id.done_button)?.setOnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 2cfd075..10e67a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -20,6 +20,7 @@
import android.animation.ValueAnimator
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.IMPORTANCE_NONE
import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
import android.content.Context
@@ -55,12 +56,14 @@
// The first row is for the entire app
private lateinit var appControlRow: AppControlView
+ private lateinit var channelListView: LinearLayout
private val channelRows = mutableListOf<ChannelRow>()
override fun onFinishInflate() {
super.onFinishInflate()
appControlRow = requireViewById(R.id.app_control)
+ channelListView = requireViewById(R.id.scrollView)
}
/**
@@ -102,7 +105,7 @@
// Remove any rows
for (row in channelRows) {
- removeView(row)
+ channelListView.removeView(row)
}
channelRows.clear()
@@ -122,7 +125,7 @@
row.channel = channel
channelRows.add(row)
- addView(row)
+ channelListView.addView(row)
}
private fun updateAppControlRow(enabled: Boolean) {
@@ -179,7 +182,9 @@
switch = requireViewById(R.id.toggle)
switch.setOnCheckedChangeListener { _, b ->
channel?.let {
- controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
+ controller.proposeEditForChannel(it,
+ if (b) it.originalImportance.coerceAtLeast(IMPORTANCE_LOW)
+ else IMPORTANCE_NONE)
}
}
setOnClickListener { switch.toggle() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index fb8024c..d18f991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2658,42 +2658,6 @@
}
/**
- * Returns the number of channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public int getNumUniqueChannels() {
- return getUniqueChannels().size();
- }
-
- /**
- * Returns the channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public ArraySet<NotificationChannel> getUniqueChannels() {
- ArraySet<NotificationChannel> channels = new ArraySet<>();
-
- channels.add(mEntry.getChannel());
-
- // If this is a summary, then add in the children notification channels for the
- // same user and pkg.
- if (mIsSummaryWithChildren) {
- final List<ExpandableNotificationRow> childrenRows = getAttachedChildren();
- final int numChildren = childrenRows.size();
- for (int i = 0; i < numChildren; i++) {
- final ExpandableNotificationRow childRow = childrenRows.get(i);
- final NotificationChannel childChannel = childRow.getEntry().getChannel();
- final StatusBarNotification childSbn = childRow.getEntry().getSbn();
- if (childSbn.getUser().equals(mEntry.getSbn().getUser())
- && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) {
- channels.add(childChannel);
- }
- }
- }
-
- return channels;
- }
-
- /**
* If this is a group, update the appearance of the children.
*/
public void updateChildrenAppearance() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 1dd3739..6d65660 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -403,7 +403,6 @@
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
onAppSettingsClick,
@@ -449,7 +448,6 @@
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index d8f31d4..d8ebd42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -104,8 +104,6 @@
private String mAppName;
private int mAppUid;
private String mDelegatePkg;
- private int mNumUniqueChannelsInRow;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
private boolean mWasShownHighPriority;
@@ -196,7 +194,6 @@
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
@@ -213,8 +210,6 @@
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
mPackageName = pkg;
- mUniqueChannelsInRow = uniqueChannelsInRow;
- mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
mEntry = entry;
mSbn = entry.getSbn();
mPm = pm;
@@ -236,15 +231,8 @@
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
- if (mNumUniqueChannelsInRow == 0) {
- throw new IllegalArgumentException("bindNotification requires at least one channel");
- } else {
- // Special behavior for the Default channel if no other channels have been defined.
- mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
- && mSingleNotificationChannel.getId().equals(
- NotificationChannel.DEFAULT_CHANNEL_ID)
- && numTotalChannels == 1;
- }
+ mIsSingleDefaultChannel = mSingleNotificationChannel.getId().equals(
+ NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1;
mIsAutomaticChosen = getAlertingBehavior() == BEHAVIOR_AUTOMATIC;
bindHeader();
@@ -271,11 +259,6 @@
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
findViewById(R.id.turn_off_notifications).setVisibility(GONE);
- } else if (mNumUniqueChannelsInRow > 1) {
- findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
- findViewById(R.id.non_configurable_text).setVisibility(GONE);
- findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
- findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
} else {
findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
findViewById(R.id.non_configurable_text).setVisibility(GONE);
@@ -361,9 +344,7 @@
if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
final int appUidF = mAppUid;
return ((View view) -> {
- mOnSettingsClickListener.onClick(view,
- mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
- appUidF);
+ mOnSettingsClickListener.onClick(view, mSingleNotificationChannel, appUidF);
});
}
return null;
@@ -375,7 +356,7 @@
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
@@ -392,7 +373,7 @@
private void bindName() {
final TextView channelName = findViewById(R.id.channel_name);
- if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+ if (mIsSingleDefaultChannel) {
channelName.setVisibility(View.GONE);
} else {
channelName.setText(mSingleNotificationChannel.getName());
@@ -459,7 +440,7 @@
Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
bgHandler.post(
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
- mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+ mSingleNotificationChannel,
mStartingChannelImportance, newImportance, mIsAutomaticChosen));
mOnUserInteractionCallback.onImportanceChanged(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 06c3b79..53f7d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -57,7 +57,6 @@
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
private boolean mIsNonBlockable;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private Drawable mPkgIcon;
private boolean mPresentingChannelEditorDialog = false;
@@ -83,7 +82,6 @@
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
NotificationInfo.OnSettingsClickListener onSettingsClick,
boolean isDeviceProvisioned,
@@ -100,7 +98,6 @@
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNonBlockable = isNonBlockable;
mChannelEditorDialogController = channelEditorDialogController;
- mUniqueChannelsInRow = uniqueChannelsInRow;
bindHeader();
bindActions();
@@ -149,7 +146,7 @@
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 485ab32..f7ff39c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -75,6 +75,7 @@
import dagger.Lazy;
+import java.util.Arrays;
import java.util.Optional;
import javax.inject.Inject;
@@ -201,6 +202,11 @@
}
@Override
+ public void setQsTiles(String[] tiles) {
+ mQSHost.changeTilesByUser(mQSHost.getSpecs(), Arrays.stream(tiles).toList());
+ }
+
+ @Override
public void clickTile(ComponentName tile) {
// Can't inject this because it changes with the QS fragment
QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
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 6f69ea81..05beded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -24,9 +24,11 @@
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -121,7 +123,6 @@
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
@@ -165,8 +166,9 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
@@ -244,8 +246,6 @@
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -257,6 +257,8 @@
import javax.inject.Named;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -1346,9 +1348,10 @@
});
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
- if (qs instanceof QSFragment) {
- mQSPanelController = ((QSFragment) qs).getQSPanelController();
- ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
+ if (qs instanceof QSFragmentLegacy) {
+ QSFragmentLegacy qsFragment = (QSFragmentLegacy) qs;
+ mQSPanelController = qsFragment.getQSPanelController();
+ qsFragment.setBrightnessMirrorController(mBrightnessMirrorController);
}
});
}
@@ -1502,7 +1505,7 @@
protected QS createDefaultQSFragment() {
return mFragmentService
.getFragmentHostManager(getNotificationShadeWindowView())
- .create(QSFragment.class);
+ .create(QSFragmentLegacy.class);
}
private void setUpPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b53939e..a27e67b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -112,6 +112,9 @@
mDisplayCutout = null;
}
+ // Per b/300629388, we let the PhoneStatusBarView detect onConfigurationChanged to
+ // updateResources, instead of letting the PhoneStatusBarViewController detect onConfigChanged
+ // then notify PhoneStatusBarView.
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index e1096e2..f9702dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone
import android.app.StatusBarManager.WINDOW_STATUS_BAR
-import android.content.res.Configuration
import android.graphics.Point
import android.util.Log
import android.view.MotionEvent
@@ -72,10 +71,6 @@
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- mView.updateResources()
- }
-
override fun onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 02473f2..aacdc63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -186,6 +186,10 @@
}
)
}
+
+ fun logOnSimStateChanged() {
+ buffer.log(TAG, LogLevel.INFO, "onSimStateChanged")
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index ea77163..cf1c97c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -90,4 +90,12 @@
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
val defaultMobileIconGroup: Flow<MobileIconGroup>
+
+ /**
+ * If any active SIM on the device is in
+ * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED]
+ */
+ val isAnySimSecure: Flow<Boolean>
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 991ff56..2291631 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -151,6 +151,8 @@
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
+ override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
override val defaultDataSubId: StateFlow<Int> =
activeRepo
.flatMapLatest { it.defaultDataSubId }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index ee13d93..c7987e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -134,6 +134,8 @@
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
+ override val isAnySimSecure: Flow<Boolean> = flowOf(false)
+
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
/**
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 cd68621..dc50990 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
@@ -71,6 +71,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
@@ -181,6 +182,7 @@
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.scan(initial = initial) { state, event -> state.applyEvent(event) }
.stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
}
@@ -358,6 +360,7 @@
awaitClose { context.unregisterReceiver(receiver) }
}
+ .flowOn(bgDispatcher)
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled = run {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 74a849a..ecb80f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -28,9 +28,10 @@
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -38,6 +39,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
@@ -94,6 +96,7 @@
// See [CarrierMergedConnectionRepository] for details.
wifiRepository: WifiRepository,
private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : MobileConnectionsRepository {
private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
mutableMapOf()
@@ -134,22 +137,24 @@
)
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
- private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
- val callback =
- object : SubscriptionManager.OnSubscriptionsChangedListener() {
- override fun onSubscriptionsChanged() {
- logger.logOnSubscriptionsChanged()
- trySend(Unit)
- }
+ private val mobileSubscriptionsChangeEvent: Flow<Unit> =
+ conflatedCallbackFlow {
+ val callback =
+ object : SubscriptionManager.OnSubscriptionsChangedListener() {
+ override fun onSubscriptionsChanged() {
+ logger.logOnSubscriptionsChanged()
+ trySend(Unit)
+ }
+ }
+
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+
+ awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
}
-
- subscriptionManager.addOnSubscriptionsChangedListener(
- bgDispatcher.asExecutor(),
- callback,
- )
-
- awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
- }
+ .flowOn(bgDispatcher)
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
@@ -184,6 +189,7 @@
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
@@ -250,6 +256,27 @@
.distinctUntilChanged()
.onEach { logger.logDefaultMobileIconGroup(it) }
+ override val isAnySimSecure: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
+ logger.logOnSimStateChanged()
+ trySend(keyguardUpdateMonitor.isSimPinSecure)
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySend(false)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .logDiffsForTable(
+ tableLogger,
+ LOGGING_PREFIX,
+ columnName = "isAnySimSecure",
+ initialValue = false,
+ )
+ .distinctUntilChanged()
+
override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
getOrCreateRepoForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index e489499..ce9d6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -37,7 +37,6 @@
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -50,6 +49,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.CreateUserActivity
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
@@ -62,6 +62,7 @@
import com.android.systemui.user.utils.MultiUserActionsEvent
import com.android.systemui.user.utils.MultiUserActionsEventHelper
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.UserRestrictionChecker
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -103,6 +104,7 @@
private val refreshUsersScheduler: RefreshUsersScheduler,
private val guestUserInteractor: GuestUserInteractor,
private val uiEventLogger: UiEventLogger,
+ private val userRestrictionChecker: UserRestrictionChecker,
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -593,6 +595,7 @@
) &&
// If the user is auto-created is must not be currently resetting.
!(isGuestUserAutoCreated && isGuestUserResetting),
+ userRestrictionChecker = userRestrictionChecker,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 93573fa..80139bd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -22,10 +22,10 @@
import android.graphics.Bitmap
import android.os.UserManager
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.systemui.res.R
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.utils.UserRestrictionChecker
/**
* Defines utility functions for helping with legacy data code for users.
@@ -68,6 +68,7 @@
actionType: UserActionModel,
isRestricted: Boolean,
isSwitchToEnabled: Boolean,
+ userRestrictionChecker: UserRestrictionChecker,
): UserRecord {
return UserRecord(
isGuest = actionType == UserActionModel.ENTER_GUEST_MODE,
@@ -79,6 +80,7 @@
getEnforcedAdmin(
context = context,
selectedUserId = selectedUserId,
+ userRestrictionChecker = userRestrictionChecker,
),
isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
)
@@ -103,9 +105,10 @@
private fun getEnforcedAdmin(
context: Context,
selectedUserId: Int,
+ userRestrictionChecker: UserRestrictionChecker
): EnforcedAdmin? {
val admin =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ userRestrictionChecker.checkIfRestrictionEnforced(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
@@ -113,7 +116,7 @@
?: return null
return if (
- !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ !userRestrictionChecker.hasBaseUserRestriction(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
diff --git a/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
new file mode 100644
index 0000000..3f8346b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.utils
+
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
+import javax.inject.Inject
+
+/** Proxy to call [RestrictedLockUtilsInternal] */
+class UserRestrictionChecker @Inject constructor() {
+ fun checkIfRestrictionEnforced(
+ context: Context,
+ userRestriction: String,
+ userId: Int
+ ): RestrictedLockUtils.EnforcedAdmin? {
+ return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ userRestriction,
+ userId
+ )
+ }
+
+ fun hasBaseUserRestriction(context: Context, userRestriction: String, userId: Int): Boolean {
+ return RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 8fc63b2..7365460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -126,6 +127,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
shadeInteractor =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index c0257df..92c8a39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -395,7 +395,7 @@
val bouncerSceneKey = currentScene?.key
assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
}
@@ -409,7 +409,7 @@
val notBouncerSceneKey = currentScene?.key
assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 360fa56..944b059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -32,7 +32,6 @@
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -48,6 +47,10 @@
import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.eq
@@ -87,6 +90,7 @@
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var biometricManager: BiometricManager
+ @Mock private lateinit var tableLogger: TableLogBuffer
@Captor
private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -97,6 +101,7 @@
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -112,6 +117,8 @@
devicePostureRepository = FakeDevicePostureRepository()
facePropertyRepository = FakeFacePropertyRepository()
fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
}
private suspend fun createBiometricSettingsRepository() {
@@ -132,6 +139,7 @@
dumpManager = dumpManager,
facePropertyRepository = facePropertyRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
+ mobileConnectionsRepository = mobileConnectionsRepository,
)
testScope.runCurrent()
fingerprintPropertyRepository.setProperties(
@@ -421,6 +429,50 @@
}
@Test
+ fun anySimSecure_disablesFaceAuth() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+ createBiometricSettingsRepository()
+
+ faceAuthIsEnabledByBiometricManager()
+ doNotDisableKeyguardAuthFeatures()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+
+ val isFaceAuthEnabledAndEnrolled by
+ collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
+ }
+
+ @Test
+ fun anySimSecure_disablesFaceAuthToNotCurrentlyRun() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+
+ createBiometricSettingsRepository()
+ val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
+
+ deviceIsInPostureThatSupportsFaceAuth()
+ doNotDisableKeyguardAuthFeatures()
+ faceAuthIsStrongBiometric()
+ faceAuthIsEnabledByBiometricManager()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+
+ onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
+ onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+ assertThat(isFaceAuthCurrentlyAllowed).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ assertThat(isFaceAuthCurrentlyAllowed).isFalse()
+ }
+
+ @Test
fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
testScope.runTest {
faceAuthIsEnrolled()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
similarity index 72%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
index 93f316e..9e5d3bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
@@ -29,15 +29,16 @@
import org.mockito.Mockito.mock
@SmallTest
-class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
+class QSDisableFlagsLoggerTest : SysuiTestCase() {
- private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- private val disableFlagsLogger = DisableFlagsLogger(
- listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
- listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
- )
- private val logger = QSFragmentDisableFlagsLogger(buffer, disableFlagsLogger)
+ private val buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+ private val disableFlagsLogger =
+ DisableFlagsLogger(
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+ )
+ private val logger = QSDisableFlagsLogger(buffer, disableFlagsLogger)
@Test
fun logDisableFlagChange_bufferHasStates() {
@@ -48,9 +49,8 @@
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
val actualString = stringWriter.toString()
- val expectedLogString = disableFlagsLogger.getDisableFlagsString(
- new = state, newAfterLocalModification = state
- )
+ val expectedLogString =
+ disableFlagsLogger.getDisableFlagsString(new = state, newAfterLocalModification = state)
assertThat(actualString).contains(expectedLogString)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
similarity index 67%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index c4c233c..d57765c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -1,15 +1,17 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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
+ * 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.
+ * 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.qs;
@@ -34,7 +36,6 @@
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
-import android.app.Fragment;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
@@ -49,12 +50,12 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.res.R;
-import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
@@ -66,8 +67,8 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
@@ -79,10 +80,9 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-public class QSFragmentTest extends SysuiBaseFragmentTest {
+public class QSImplTest extends SysuiTestCase {
- @Mock private QSFragmentComponent.Factory mQsComponentFactory;
- @Mock private QSFragmentComponent mQsFragmentComponent;
+ @Mock private QSComponent mQsComponent;
@Mock private QSPanelController mQSPanelController;
@Mock private MediaHost mQSMediaHost;
@Mock private MediaHost mQQSMediaHost;
@@ -107,69 +107,54 @@
@Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
@Mock private FooterActionsViewBinder mFooterActionsViewBinder;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
- private View mQsFragmentView;
+ @Mock private FeatureFlagsClassic mFeatureFlags;
+ private View mQsView;
- public QSFragmentTest() {
- super(QSFragment.class);
- }
+ private QSImpl mUnderTest;
+
@Before
public void setup() {
- injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ mUnderTest = instantiate();
+
+ mUnderTest.onComponentCreated(mQsComponent, null);
}
- @Test
- public void testListening() {
- QSFragment qs = (QSFragment) mFragment;
- mFragments.dispatchResume();
- processAllMessages();
-
- qs.setListening(true);
- processAllMessages();
-
- qs.setListening(false);
- processAllMessages();
- }
@Test
public void testSaveState() {
- mFragments.dispatchResume();
- processAllMessages();
+ mUnderTest.setListening(true);
+ mUnderTest.setExpanded(true);
+ mUnderTest.setQsVisible(true);
- QSFragment qs = (QSFragment) mFragment;
- qs.setListening(true);
- qs.setExpanded(true);
- qs.setQsVisible(true);
- processAllMessages();
- recreateFragment();
- processAllMessages();
+ Bundle bundle = new Bundle();
+ mUnderTest.onSaveInstanceState(bundle);
- // Get the reference to the new fragment.
- qs = (QSFragment) mFragment;
- assertTrue(qs.isListening());
- assertTrue(qs.isExpanded());
- assertTrue(qs.isQsVisible());
+ // Get a new instance
+ QSImpl other = instantiate();
+ other.onComponentCreated(mQsComponent, bundle);
+
+ assertTrue(other.isListening());
+ assertTrue(other.isExpanded());
+ assertTrue(other.isQsVisible());
}
@Test
public void transitionToFullShade_smallScreen_alphaAlways1() {
- QSFragment fragment = resumeAndGetFragment();
setIsSmallScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1f);
+ assertThat(mQsView.getAlpha()).isEqualTo(1f);
}
@Test
public void transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setIsLargeScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
@@ -177,43 +162,40 @@
float squishinessFraction = 0.5f;
when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f);
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha())
- .isEqualTo(123f);
+ assertThat(mQsView.getAlpha()).isEqualTo(123f);
}
@Test
public void
transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(KEYGUARD);
when(mQSPanelController.isBouncerInTransit()).thenReturn(false);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+ assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
}
@Test
public void
transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(KEYGUARD);
when(mQSPanelController.isBouncerInTransit()).thenReturn(true);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha())
+ assertThat(mQsView.getAlpha())
.isEqualTo(
BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
transitionProgress));
@@ -221,40 +203,37 @@
@Test
public void transitionToFullShade_inFullWidth_alwaysSetsAlphaTo1() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setIsNotificationPanelFullWidth(true);
+ mUnderTest.setIsNotificationPanelFullWidth(true);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.1f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
transitionProgress = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
transitionProgress = 0.7f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
}
@Test
public void transitionToFullShade_setsSquishinessOnController() {
- QSFragment fragment = resumeAndGetFragment();
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.123f;
float squishinessFraction = 0.456f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- verify(mQsFragmentComponent.getQSSquishinessController())
- .setSquishiness(squishinessFraction);
+ verify(mQsComponent.getQSSquishinessController()).setSquishiness(squishinessFraction);
}
@Test
@@ -265,10 +244,9 @@
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -283,10 +261,9 @@
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
- fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -295,7 +272,6 @@
@Test
public void setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
@@ -303,24 +279,23 @@
float transitionProgress = 0;
float squishinessFraction = 0f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
// trigger alpha refresh with non-zero expansion and fraction values
- fragment.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
+ mUnderTest.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
/* proposedTranslation= */ 0, /* squishinessFraction= */ 1);
// alpha should follow lockscreen to shade progress, not panel expansion fraction
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+ assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
}
@Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
when(mHeader.getHeight()).thenReturn(1234);
- int height = fragment.getQsMinExpansionHeight();
+ int height = mUnderTest.getQsMinExpansionHeight();
assertThat(height).isEqualTo(mHeader.getHeight());
}
@@ -329,13 +304,12 @@
public void getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomOfQSContainer() {
int top = 1234;
int height = 9876;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- setLocationOnScreen(mQsFragmentView, top);
- when(mQsFragmentView.getHeight()).thenReturn(height);
+ setLocationOnScreen(mQsView, top);
+ when(mQsView.getHeight()).thenReturn(height);
int expectedHeight = top + height;
- assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+ assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
}
@Test
@@ -343,47 +317,43 @@
int top = 1234;
int height = 9876;
float translationY = -600f;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- setLocationOnScreen(mQsFragmentView, (int) (top + translationY));
- when(mQsFragmentView.getHeight()).thenReturn(height);
- when(mQsFragmentView.getTranslationY()).thenReturn(translationY);
+ setLocationOnScreen(mQsView, (int) (top + translationY));
+ when(mQsView.getHeight()).thenReturn(height);
+ when(mQsView.getTranslationY()).thenReturn(translationY);
int expectedHeight = top + height;
- assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+ assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
}
@Test
public void hideImmediately_notInSplitShade_movesViewUpByHeaderHeight() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
when(mHeader.getHeight()).thenReturn(555);
- fragment.hideImmediately();
+ mUnderTest.hideImmediately();
- assertThat(mQsFragmentView.getY()).isEqualTo(-mHeader.getHeight());
+ assertThat(mQsView.getY()).isEqualTo(-mHeader.getHeight());
}
@Test
public void hideImmediately_inSplitShade_movesViewUpByQSAbsoluteBottom() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
int top = 1234;
int height = 9876;
- setLocationOnScreen(mQsFragmentView, top);
- when(mQsFragmentView.getHeight()).thenReturn(height);
+ setLocationOnScreen(mQsView, top);
+ when(mQsView.getHeight()).thenReturn(height);
- fragment.hideImmediately();
+ mUnderTest.hideImmediately();
int qsAbsoluteBottom = top + height;
- assertThat(mQsFragmentView.getY()).isEqualTo(-qsAbsoluteBottom);
+ assertThat(mQsView.getY()).isEqualTo(-qsAbsoluteBottom);
}
@Test
public void setCollapseExpandAction_passedToControllers() {
Runnable action = () -> {};
- QSFragment fragment = resumeAndGetFragment();
- fragment.setCollapseExpandAction(action);
+ mUnderTest.setCollapseExpandAction(action);
verify(mQSPanelController).setCollapseExpandAction(action);
verify(mQuickQSPanelController).setCollapseExpandAction(action);
@@ -391,24 +361,21 @@
@Test
public void setOverScrollAmount_setsTranslationOnView() {
- QSFragment fragment = resumeAndGetFragment();
+ mUnderTest.setOverScrollAmount(123);
- fragment.setOverScrollAmount(123);
-
- assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123);
+ assertThat(mQsView.getTranslationY()).isEqualTo(123);
}
@Test
public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() {
- QSFragment fragment = getFragment();
+ QSImpl other = instantiate();
+ other.setOverScrollAmount(123);
- fragment.setOverScrollAmount(123);
-
- assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0);
+ assertThat(mQsView.getTranslationY()).isEqualTo(0);
}
private Lifecycle.State getListeningAndVisibilityLifecycleState() {
- return getFragment()
+ return mUnderTest
.getListeningAndVisibilityLifecycleOwner()
.getLifecycle()
.getCurrentState();
@@ -416,11 +383,10 @@
@Test
public void setListeningFalse_notVisible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(false);
+ mUnderTest.setQsVisible(false);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(false);
+ mUnderTest.setListening(false);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -428,11 +394,10 @@
@Test
public void setListeningTrue_notVisible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(false);
+ mUnderTest.setQsVisible(false);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(true);
+ mUnderTest.setListening(true);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -440,11 +405,10 @@
@Test
public void setListeningFalse_visible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(false);
+ mUnderTest.setListening(false);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -452,11 +416,10 @@
@Test
public void setListeningTrue_visible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(true);
+ mUnderTest.setListening(true);
verify(mQSContainerImplController).setListening(true);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED);
verify(mQSPanelController).setListening(eq(true), anyBoolean());
@@ -464,31 +427,28 @@
@Test
public void passCorrectExpansionState_inSplitShade() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
clearInvocations(mQSPanelController);
- fragment.setExpanded(true);
+ mUnderTest.setExpanded(true);
verify(mQSPanelController).setExpanded(true);
- fragment.setExpanded(false);
+ mUnderTest.setExpanded(false);
verify(mQSPanelController).setExpanded(false);
}
@Test
public void startsListeningAfterStateChangeToExpanded_inSplitShade() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSPanelController);
- fragment.setExpanded(true);
+ mUnderTest.setExpanded(true);
verify(mQSPanelController).setListening(true, true);
}
@Test
public void testUpdateQSBounds_setMediaClipCorrectly() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
Rect mediaHostClip = new Rect();
@@ -497,7 +457,7 @@
when(mQSPanelScrollView.getMeasuredHeight()).thenReturn(200);
when(mQSMediaHost.getCurrentClipping()).thenReturn(mediaHostClip);
- fragment.updateQsBounds();
+ mUnderTest.updateQsBounds();
assertEquals(25, mediaHostClip.top);
assertEquals(175, mediaHostClip.bottom);
@@ -505,17 +465,15 @@
@Test
public void testQsUpdatesQsAnimatorWithUpcomingState() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(SHADE);
- fragment.onUpcomingStateChanged(KEYGUARD);
+ mUnderTest.onUpcomingStateChanged(KEYGUARD);
verify(mQSAnimator).setOnKeyguard(true);
}
- @Override
- protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ private QSImpl instantiate() {
MockitoAnnotations.initMocks(this);
- CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context));
+ CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
setupQsComponent();
setUpViews();
@@ -523,9 +481,9 @@
setUpMedia();
setUpOther();
- return new QSFragment(
+ return new QSImpl(
new RemoteInputQuickSettingsDisabler(
- context,
+ mContext,
commandQueue,
new ResourcesSplitShadeStateController(),
mock(ConfigurationController.class)),
@@ -534,8 +492,7 @@
mQSMediaHost,
mQQSMediaHost,
mBypassController,
- mQsComponentFactory,
- mock(QSFragmentDisableFlagsLogger.class),
+ mock(QSDisableFlagsLogger.class),
mock(DumpManager.class),
mock(QSLogger.class),
mock(FooterActionsController.class),
@@ -561,12 +518,13 @@
}
private void setUpViews() {
- mQsFragmentView = spy(new View(mContext));
- when(mQsFragmentView.findViewById(R.id.expanded_qs_scroll_view))
+ mQsView = spy(new View(mContext));
+ when(mQsComponent.getRootView()).thenReturn(mQsView);
+ when(mQsView.findViewById(R.id.expanded_qs_scroll_view))
.thenReturn(mQSPanelScrollView);
- when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader);
- when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
- when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer(
+ when(mQsView.findViewById(R.id.header)).thenReturn(mHeader);
+ when(mQsView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
+ when(mQsView.findViewById(R.id.qs_footer_actions)).thenAnswer(
invocation -> new FooterActionsViewBinder().create(mContext));
}
@@ -597,37 +555,26 @@
return realInflater.inflate(layoutRes, root, attachToRoot);
}
- return mQsFragmentView;
+ return mQsView;
}
private void setupQsComponent() {
- when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
- when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
- when(mQsFragmentComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
- when(mQsFragmentComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
- when(mQsFragmentComponent.getQSContainerImplController())
+ when(mQsComponent.getQSPanelController()).thenReturn(mQSPanelController);
+ when(mQsComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
+ when(mQsComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
+ when(mQsComponent.getQSContainerImplController())
.thenReturn(mQSContainerImplController);
- when(mQsFragmentComponent.getQSFooter()).thenReturn(mFooter);
- when(mQsFragmentComponent.getQSFooterActionController())
+ when(mQsComponent.getQSFooter()).thenReturn(mFooter);
+ when(mQsComponent.getQSFooterActionController())
.thenReturn(mQSFooterActionController);
- when(mQsFragmentComponent.getQSAnimator()).thenReturn(mQSAnimator);
- when(mQsFragmentComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
- }
-
- private QSFragment getFragment() {
- return ((QSFragment) mFragment);
- }
-
- private QSFragment resumeAndGetFragment() {
- mFragments.dispatchResume();
- processAllMessages();
- return getFragment();
+ when(mQsComponent.getQSAnimator()).thenReturn(mQSAnimator);
+ when(mQsComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
}
private void setStatusBarCurrentAndUpcomingState(int statusBarState) {
when(mStatusBarStateController.getState()).thenReturn(statusBarState);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(statusBarState);
- getFragment().onStateChanged(statusBarState);
+ mUnderTest.onStateChanged(statusBarState);
}
private void enableSplitShade() {
@@ -639,7 +586,7 @@
}
private void setSplitShadeEnabled(boolean enabled) {
- getFragment().setInSplitShade(enabled);
+ mUnderTest.setInSplitShade(enabled);
}
private void setLocationOnScreen(View view, int top) {
@@ -652,10 +599,10 @@
}
private void setIsLargeScreen() {
- getFragment().setIsNotificationPanelFullWidth(false);
+ mUnderTest.setIsNotificationPanelFullWidth(false);
}
private void setIsSmallScreen() {
- getFragment().setIsNotificationPanelFullWidth(true);
+ mUnderTest.setIsNotificationPanelFullWidth(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 9386d71..9a55f72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -23,15 +23,19 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -39,6 +43,20 @@
@RunWith(AndroidJUnit4::class)
class AutoAddSettingsRepositoryTest : SysuiTestCase() {
private val secureSettings = FakeSettings()
+ private val userAutoAddRepositoryFactory =
+ object : UserAutoAddRepository.Factory {
+ override fun create(userId: Int): UserAutoAddRepository {
+ return UserAutoAddRepository(
+ userId,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+ }
+
+ @Mock private lateinit var logger: QSPipelineLogger
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -47,110 +65,37 @@
@Before
fun setUp() {
- underTest =
- AutoAddSettingRepository(
- secureSettings,
- testDispatcher,
- )
+ MockitoAnnotations.initMocks(this)
+
+ underTest = AutoAddSettingRepository(userAutoAddRepositoryFactory)
}
@Test
- fun nonExistentSetting_emptySet() =
- testScope.runTest {
- val specs by collectLastValue(underTest.autoAddedTiles(0))
-
- assertThat(specs).isEmpty()
- }
-
- @Test
- fun settingsChange_correctValues() =
- testScope.runTest {
- val userId = 0
- val specs by collectLastValue(underTest.autoAddedTiles(userId))
-
- val value = "a,custom(b/c)"
- storeForUser(value, userId)
-
- assertThat(specs).isEqualTo(value.toSet())
-
- val newValue = "a"
- storeForUser(newValue, userId)
-
- assertThat(specs).isEqualTo(newValue.toSet())
- }
-
- @Test
fun tilesForCorrectUsers() =
testScope.runTest {
- val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
- val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
-
val user0Tiles = "a"
val user1Tiles = "custom(b/c)"
storeForUser(user0Tiles, 0)
storeForUser(user1Tiles, 1)
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
- assertThat(tilesFromUser0).isEqualTo(user0Tiles.toSet())
- assertThat(tilesFromUser1).isEqualTo(user1Tiles.toSet())
- }
-
- @Test
- fun noInvalidTileSpecs() =
- testScope.runTest {
- val userId = 0
- val tiles by collectLastValue(underTest.autoAddedTiles(userId))
-
- val specs = "d,custom(bad)"
- storeForUser(specs, userId)
-
- assertThat(tiles).isEqualTo("d".toSet())
- }
-
- @Test
- fun markAdded() =
- testScope.runTest {
- val userId = 0
- val specs = mutableSetOf(TileSpec.create("a"))
- underTest.markTileAdded(userId, TileSpec.create("a"))
-
- assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
-
- specs.add(TileSpec.create("b"))
- underTest.markTileAdded(userId, TileSpec.create("b"))
-
- assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
+ assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTilesSet())
+ assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTilesSet())
}
@Test
fun markAdded_multipleUsers() =
testScope.runTest {
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
+
underTest.markTileAdded(userId = 1, TileSpec.create("a"))
- assertThat(loadForUser(0).toSet()).isEmpty()
- assertThat(loadForUser(1).toSet())
- .containsExactlyElementsIn(setOf(TileSpec.create("a")))
- }
-
- @Test
- fun markAdded_Invalid_noop() =
- testScope.runTest {
- val userId = 0
- underTest.markTileAdded(userId, TileSpec.Invalid)
-
- assertThat(loadForUser(userId).toSet()).isEmpty()
- }
-
- @Test
- fun unmarkAdded() =
- testScope.runTest {
- val userId = 0
- val specs = "a,custom(b/c)"
- storeForUser(specs, userId)
-
- underTest.unmarkTileAdded(userId, TileSpec.create("a"))
-
- assertThat(loadForUser(userId).toSet())
- .containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+ assertThat(tilesFromUser0).isEmpty()
+ assertThat(tilesFromUser1).containsExactlyElementsIn(setOf(TileSpec.create("a")))
}
@Test
@@ -159,33 +104,23 @@
val specs = "a,b"
storeForUser(specs, 0)
storeForUser(specs, 1)
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
underTest.unmarkTileAdded(1, TileSpec.create("a"))
- assertThat(loadForUser(0).toSet()).isEqualTo(specs.toSet())
- assertThat(loadForUser(1).toSet()).isEqualTo(setOf(TileSpec.create("b")))
+ assertThat(tilesFromUser0).isEqualTo(specs.toTilesSet())
+ assertThat(tilesFromUser1).isEqualTo(setOf(TileSpec.create("b")))
}
private fun storeForUser(specs: String, userId: Int) {
secureSettings.putStringForUser(SETTING, specs, userId)
}
- private fun loadForUser(userId: Int): String {
- return secureSettings.getStringForUser(SETTING, userId) ?: ""
- }
-
companion object {
private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
- private const val DELIMITER = ","
- fun Set<TileSpec>.toSeparatedString() = joinToString(DELIMITER, transform = TileSpec::spec)
-
- fun String.toSet(): Set<TileSpec> {
- return if (isNullOrBlank()) {
- emptySet()
- } else {
- split(DELIMITER).map(TileSpec::create).toSet()
- }
- }
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
new file mode 100644
index 0000000..dc09a33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -0,0 +1,220 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@RoboPilotTest
+class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
+ private val dispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+
+ @Mock private lateinit var pipelineLogger: QSPipelineLogger
+
+ private lateinit var underTest: QSSettingsRestoredBroadcastRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ QSSettingsRestoredBroadcastRepository(
+ fakeBroadcastDispatcher,
+ pipelineLogger,
+ testScope.backgroundScope,
+ dispatcher,
+ )
+ }
+
+ @Test
+ fun restoreDataAfterBothIntents_tilesRestoredFirst() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ // No restore data yet as we are missing one of the broadcasts
+ assertThat(restoreData).isNull()
+
+ // After the second event, we see the corresponding restore
+ sendIntentForUser(autoAddIntent, user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun restoreDataAfterBothIntents_autoAddRestoredFirst() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(autoAddIntent, user)
+
+ // No restore data yet as we are missing one of the broadcasts
+ assertThat(restoreData).isNull()
+
+ // After the second event, we see the corresponding restore
+ sendIntentForUser(tilesIntent, user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun interleavedBroadcastsFromDifferentUsers_onlysendDataForCorrectUser() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+
+ val user0 = 0
+ val user10 = 10
+
+ val currentTiles10 = "z,y,x"
+ val restoredTiles10 = "x"
+ val currentAutoAdded10 = "f"
+ val restoredAutoAdded10 = "f,g"
+
+ val tilesIntent0 =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+ val autoAddIntent0 =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+ val tilesIntent10 =
+ createRestoreIntent(
+ RestoreType.TILES,
+ currentTiles10,
+ restoredTiles10,
+ )
+ val autoAddIntent10 =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ currentAutoAdded10,
+ restoredAutoAdded10,
+ )
+
+ sendIntentForUser(tilesIntent0, user0)
+ sendIntentForUser(autoAddIntent10, user10)
+ assertThat(restoreData).isNull()
+
+ sendIntentForUser(tilesIntent10, user10)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(restoredTiles10.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(restoredAutoAdded10.toTilesSet())
+ assertThat(userId).isEqualTo(user10)
+ }
+
+ sendIntentForUser(autoAddIntent0, user0)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user0)
+ }
+ }
+
+ private fun sendIntentForUser(intent: Intent, userId: Int) {
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ intent,
+ FakeBroadcastDispatcher.fakePendingResultForUser(userId)
+ )
+ }
+
+ companion object {
+ private const val CURRENT_TILES = "a,b,c,d"
+ private const val RESTORED_TILES = "b,a,c"
+ private const val CURRENT_AUTO_ADDED_TILES = "d"
+ private const val RESTORED_AUTO_ADDED_TILES = "e"
+
+ private fun createRestoreIntent(
+ type: RestoreType,
+ previousValue: String,
+ restoredValue: String,
+ ): Intent {
+ val setting =
+ when (type) {
+ RestoreType.TILES -> Settings.Secure.QS_TILES
+ RestoreType.AUTOADD -> Settings.Secure.QS_AUTO_ADDED_TILES
+ }
+ return Intent(Intent.ACTION_SETTING_RESTORED)
+ .putExtra(Intent.EXTRA_SETTING_NAME, setting)
+ .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
+ .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue)
+ }
+
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+
+ private enum class RestoreType {
+ TILES,
+ AUTOADD,
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 1c28e4c..08adebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -23,14 +23,12 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.QSHost
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.retail.data.repository.FakeRetailModeRepository
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -49,9 +47,28 @@
private lateinit var secureSettings: FakeSettings
private lateinit var retailModeRepository: FakeRetailModeRepository
+ private val defaultTilesRepository =
+ object : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() = DEFAULT_TILES.toTileSpecs()
+ }
@Mock private lateinit var logger: QSPipelineLogger
+ private val userTileSpecRepositoryFactory =
+ object : UserTileSpecRepository.Factory {
+ override fun create(userId: Int): UserTileSpecRepository {
+ return UserTileSpecRepository(
+ userId,
+ defaultTilesRepository,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+ }
+
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -66,293 +83,85 @@
retailModeRepository.setRetailMode(false)
with(context.orCreateTestableResources) {
- addOverride(R.string.quick_settings_tiles_default, DEFAULT_TILES)
addOverride(R.string.quick_settings_tiles_retail_mode, RETAIL_TILES)
}
underTest =
TileSpecSettingsRepository(
- secureSettings,
context.resources,
logger,
retailModeRepository,
- testDispatcher,
+ userTileSpecRepositoryFactory
)
}
@Test
- fun emptySetting_usesDefaultValue() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
- }
-
- @Test
- fun changeInSettings_changesValue() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a", 0)
- assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
-
- storeTilesForUser("a,custom(b/c)", 0)
- assertThat(tiles)
- .isEqualTo(listOf(TileSpec.create("a"), TileSpec.create("custom(b/c)")))
- }
-
- @Test
fun tilesForCorrectUsers() =
testScope.runTest {
- val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
- val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
-
val user0Tiles = "a"
val user1Tiles = "custom(b/c)"
storeTilesForUser(user0Tiles, 0)
storeTilesForUser(user1Tiles, 1)
+ val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
+ val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
+
assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTileSpecs())
assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTileSpecs())
}
@Test
- fun invalidTilesAreNotPresent() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "d,custom(bad)"
- storeTilesForUser(specs, 0)
-
- assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
- }
-
- @Test
- fun noValidTiles_defaultSet() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("custom(bad),custom()", 0)
-
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
- }
-
- @Test
- fun addTileAtEnd() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a", 0)
-
- underTest.addTile(userId = 0, TileSpec.create("b"))
-
- val expected = "a,b"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
- fun addTileAtPosition() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,custom(b/c)", 0)
-
- underTest.addTile(userId = 0, TileSpec.create("d"), position = 1)
-
- val expected = "a,d,custom(b/c)"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
- fun addInvalidTile_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
- storeTilesForUser(specs, 0)
-
- underTest.addTile(userId = 0, TileSpec.Invalid)
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun addTileAtPosition_tooLarge_addedAtEnd() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
- storeTilesForUser(specs, 0)
-
- underTest.addTile(userId = 0, TileSpec.create("d"), position = 100)
-
- val expected = "a,custom(b/c),d"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
fun addTileForOtherUser_addedInThatUser() =
testScope.runTest {
- val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
- val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
-
storeTilesForUser("a", 0)
storeTilesForUser("b", 1)
+ val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
+ val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.addTile(userId = 1, TileSpec.create("c"))
- assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(tilesUser0).isEqualTo("a".toTileSpecs())
- assertThat(loadTilesForUser(1)).isEqualTo("b,c")
+ assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(tilesUser1).isEqualTo("b,c".toTileSpecs())
- }
-
- @Test
- fun removeTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,b", 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
-
- assertThat(loadTilesForUser(0)).isEqualTo("b")
- assertThat(tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun removeTilesNotThere_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b"
- storeTilesForUser(specs, 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun removeInvalidTile_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b"
- storeTilesForUser(specs, 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.Invalid))
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b,c")
}
@Test
fun removeTileFromSecondaryUser_removedOnlyInCorrectUser() =
testScope.runTest {
- val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
- val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
val specs = "a,b"
storeTilesForUser(specs, 0)
storeTilesForUser(specs, 1)
+ val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+ val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.removeTiles(userId = 1, listOf(TileSpec.create("a")))
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
- assertThat(loadTilesForUser(1)).isEqualTo("b")
+ assertThat(loadTilesForUser(0)).isEqualTo(specs)
assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun removeMultipleTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,b,c,d", 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a"), TileSpec.create("c")))
-
- assertThat(loadTilesForUser(0)).isEqualTo("b,d")
- assertThat(tiles).isEqualTo("b,d".toTileSpecs())
- }
-
- @Test
- fun changeTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
-
- underTest.setTiles(userId = 0, specs.toTileSpecs())
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun changeTiles_ignoresInvalid() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
-
- underTest.setTiles(userId = 0, listOf(TileSpec.Invalid) + specs.toTileSpecs())
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun changeTiles_empty_noChanges() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- underTest.setTiles(userId = 0, emptyList())
-
- assertThat(loadTilesForUser(0)).isNull()
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b")
}
@Test
fun changeTiles_forCorrectUser() =
testScope.runTest {
- val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
- val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
val specs = "a"
storeTilesForUser(specs, 0)
storeTilesForUser(specs, 1)
+ val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+ val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.setTiles(userId = 1, "b".toTileSpecs())
- assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTilesForUser(0)).isEqualTo("a")
- assertThat(loadTilesForUser(1)).isEqualTo("b")
assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun multipleConcurrentRemovals_bothRemoved() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b,c"
- storeTilesForUser(specs, 0)
-
- coroutineScope {
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
- }
-
- assertThat(loadTilesForUser(0)).isEqualTo("b")
- assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b")
}
@Test
@@ -361,6 +170,7 @@
retailModeRepository.setRetailMode(true)
val tiles by collectLastValue(underTest.tilesSpecs(0))
+ runCurrent()
assertThat(tiles).isEqualTo(RETAIL_TILES.toTileSpecs())
}
@@ -369,25 +179,13 @@
fun retailMode_cannotModifyTiles() =
testScope.runTest {
retailModeRepository.setRetailMode(true)
-
- underTest.setTiles(0, DEFAULT_TILES.toTileSpecs())
-
- assertThat(loadTilesForUser(0)).isNull()
- }
-
- @Test
- fun emptyTilesReplacedByDefaultInSettings() =
- testScope.runTest {
val tiles by collectLastValue(underTest.tilesSpecs(0))
runCurrent()
- assertThat(loadTilesForUser(0))
- .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
- }
+ underTest.setTiles(0, listOf(TileSpec.create("a")))
- private fun getDefaultTileSpecs(): List<TileSpec> {
- return QSHost.getDefaultSpecs(context.resources).map(TileSpec::create)
- }
+ assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
+ }
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
@@ -403,8 +201,6 @@
private const val RETAIL_TILES = "d"
private const val SETTING = Settings.Secure.QS_TILES
- private fun String.toTileSpecs(): List<TileSpec> {
- return split(",").map(TileSpec::create)
- }
+ private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
new file mode 100644
index 0000000..2087623
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TilesSettingConverterTest : SysuiTestCase() {
+
+ @Test
+ fun toTilesList_correctContentAndOrdering() {
+ val specString =
+ listOf(
+ "c",
+ "b",
+ "custom(x/y)",
+ "d",
+ )
+ .joinToString(DELIMITER)
+
+ val expected =
+ listOf(
+ TileSpec.create("c"),
+ TileSpec.create("b"),
+ TileSpec.create("custom(x/y)"),
+ TileSpec.create("d"),
+ )
+
+ assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesList_removesInvalid() {
+ val specString =
+ listOf(
+ "a",
+ "",
+ "b",
+ )
+ .joinToString(DELIMITER)
+ assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+ val expected =
+ listOf(
+ TileSpec.create("a"),
+ TileSpec.create("b"),
+ )
+ assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesSet_correctContent() {
+ val specString =
+ listOf(
+ "c",
+ "b",
+ "custom(x/y)",
+ "d",
+ )
+ .joinToString(DELIMITER)
+
+ val expected =
+ setOf(
+ TileSpec.create("c"),
+ TileSpec.create("b"),
+ TileSpec.create("custom(x/y)"),
+ TileSpec.create("d"),
+ )
+
+ assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesSet_removesInvalid() {
+ val specString =
+ listOf(
+ "a",
+ "",
+ "b",
+ )
+ .joinToString(DELIMITER)
+ assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+ val expected =
+ setOf(
+ TileSpec.create("a"),
+ TileSpec.create("b"),
+ )
+ assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+ }
+
+ companion object {
+ private const val DELIMITER = ","
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
new file mode 100644
index 0000000..81fd72b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
@@ -0,0 +1,160 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserAutoAddRepositoryTest : SysuiTestCase() {
+ private val secureSettings = FakeSettings()
+
+ @Mock private lateinit var logger: QSPipelineLogger
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: UserAutoAddRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ UserAutoAddRepository(
+ USER,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun nonExistentSetting_emptySet() =
+ testScope.runTest {
+ val specs by collectLastValue(underTest.autoAdded())
+
+ assertThat(specs).isEmpty()
+ }
+
+ @Test
+ fun settingsChange_noChanges() =
+ testScope.runTest {
+ val value = "a,custom(b/c)"
+ store(value)
+ val specs by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ assertThat(specs).isEqualTo(value.toTilesSet())
+
+ val newValue = "a"
+ store(newValue)
+
+ assertThat(specs).isEqualTo(value.toTilesSet())
+ }
+
+ @Test
+ fun noInvalidTileSpecs() =
+ testScope.runTest {
+ val specs = "d,custom(bad)"
+ store(specs)
+ val tiles by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ assertThat(tiles).isEqualTo("d".toTilesSet())
+ }
+
+ @Test
+ fun markAdded() =
+ testScope.runTest {
+ val specs = mutableSetOf(TileSpec.create("a"))
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.markTileAdded(TileSpec.create("a"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(specs)
+
+ specs.add(TileSpec.create("b"))
+ underTest.markTileAdded(TileSpec.create("b"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(specs)
+ }
+
+ @Test
+ fun markAdded_Invalid_noop() =
+ testScope.runTest {
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.markTileAdded(TileSpec.Invalid)
+
+ Truth.assertThat(autoAdded).isEmpty()
+ }
+
+ @Test
+ fun unmarkAdded() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ store(specs)
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.unmarkTileAdded(TileSpec.create("a"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+ }
+
+ @Test
+ fun restore_addsRestoredTiles() =
+ testScope.runTest {
+ val specs = "a,b"
+ val restored = "b,c"
+ store(specs)
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ emptyList(),
+ restored.toTilesSet(),
+ USER,
+ )
+ underTest.reconcileRestore(restoreData)
+
+ assertThat(autoAdded).containsExactlyElementsIn("a,b,c".toTilesSet())
+ }
+
+ private fun store(specs: String) {
+ secureSettings.putStringForUser(SETTING, specs, USER)
+ }
+
+ companion object {
+ private const val USER = 10
+ private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
new file mode 100644
index 0000000..389580c1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -0,0 +1,351 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class UserTileSpecRepositoryTest : SysuiTestCase() {
+ private val secureSettings = FakeSettings()
+ private val defaultTilesRepository =
+ object : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() = DEFAULT_TILES.toTileSpecs()
+ }
+
+ @Mock private lateinit var logger: QSPipelineLogger
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: UserTileSpecRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ UserTileSpecRepository(
+ USER,
+ defaultTilesRepository,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun emptySetting_usesDefaultValue() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles())
+ assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ }
+
+ @Test
+ fun changeInSettings_valueDoesntChange() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+
+ storeTiles("a,custom(b/c)")
+ assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+ }
+
+ @Test
+ fun changeInSettings_settingIsRestored() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ storeTiles("a,custom(b/c)")
+ assertThat(loadTiles()).isEqualTo("a")
+ }
+
+ @Test
+ fun invalidTilesAreNotPresent() =
+ testScope.runTest {
+ val specs = "d,custom(bad)"
+ storeTiles(specs)
+
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
+ }
+
+ @Test
+ fun noValidTiles_defaultSet() =
+ testScope.runTest {
+ storeTiles("custom(bad),custom()")
+
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ }
+
+ /*
+ * Following tests are for the possible actions that can be performed to the list of tiles.
+ * In general, the tests follow this scheme:
+ *
+ * 1. Set starting tiles in Settings
+ * 2. Start collection of flows
+ * 3. Call `runCurrent` so all collectors are started (side effects)
+ * 4. Perform operation
+ * 5. Check that the flow contains the right value
+ * 6. Check that settings contains the right value.
+ */
+
+ @Test
+ fun addTileAtEnd() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("b"))
+
+ val expected = "a,b"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun addTileAtPosition() =
+ testScope.runTest {
+ storeTiles("a,custom(b/c)")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("d"), position = 1)
+
+ val expected = "a,d,custom(b/c)"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun addInvalidTile_noop() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.Invalid)
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun addTileAtPosition_tooLarge_addedAtEnd() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("d"), position = 100)
+
+ val expected = "a,custom(b/c),d"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun removeTiles() =
+ testScope.runTest {
+ storeTiles("a,b")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("a")))
+
+ assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b")
+ }
+
+ @Test
+ fun removeTilesNotThere_noop() =
+ testScope.runTest {
+ val specs = "a,b"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("c")))
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun removeInvalidTile_noop() =
+ testScope.runTest {
+ val specs = "a,b"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.Invalid))
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun removeMultipleTiles() =
+ testScope.runTest {
+ storeTiles("a,b,c,d")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("a"), TileSpec.create("c")))
+
+ assertThat(tiles).isEqualTo("b,d".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b,d")
+ }
+
+ @Test
+ fun changeTiles() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(specs.toTileSpecs())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun changeTiles_ignoresInvalid() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(listOf(TileSpec.Invalid) + specs.toTileSpecs())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun changeTiles_empty_noChanges() =
+ testScope.runTest {
+ val specs = "a,b,c,d"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(emptyList())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun multipleConcurrentRemovals_bothRemoved() =
+ testScope.runTest {
+ val specs = "a,b,c"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ coroutineScope {
+ underTest.removeTiles(listOf(TileSpec.create("c")))
+ underTest.removeTiles(listOf(TileSpec.create("a")))
+ }
+
+ assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b")
+ }
+
+ @Test
+ fun emptyTilesReplacedByDefaultInSettings() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ assertThat(loadTiles())
+ .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
+ }
+
+ @Test
+ fun restoreDataIsProperlyReconciled() =
+ testScope.runTest {
+ // Tile b was just auto-added, so we should re-add it in position 1
+ // Tile e was auto-added before, but the user had removed it (not in the restored set).
+ // It should not be re-added
+ val specsBeforeRestore = "a,b,c,d,e"
+ val restoredSpecs = "a,c,d,f"
+ val autoAddedBeforeRestore = "b,d"
+ val restoredAutoAdded = "d,e"
+
+ storeTiles(specsBeforeRestore)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTileSpecs(),
+ restoredAutoAdded.toTilesSet(),
+ USER,
+ )
+ underTest.reconcileRestore(restoreData, autoAddedBeforeRestore.toTilesSet())
+ runCurrent()
+
+ val expected = "a,b,c,d,f"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ private fun getDefaultTileSpecs(): List<TileSpec> {
+ return defaultTilesRepository.defaultTiles
+ }
+
+ private fun TestScope.storeTiles(specs: String) {
+ secureSettings.putStringForUser(SETTING, specs, USER)
+ runCurrent()
+ }
+
+ private fun loadTiles(): String? {
+ return secureSettings.getStringForUser(SETTING, USER)
+ }
+
+ companion object {
+ private const val USER = 10
+ private const val DEFAULT_TILES = "a,b,c"
+ private const val SETTING = Settings.Secure.QS_TILES
+
+ private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
new file mode 100644
index 0000000..5630b9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -0,0 +1,94 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RestoreReconciliationInteractorTest : SysuiTestCase() {
+
+ private val tileSpecRepository = FakeTileSpecRepository()
+ private val autoAddRepository = FakeAutoAddRepository()
+
+ private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository()
+
+ private lateinit var underTest: RestoreReconciliationInteractor
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ RestoreReconciliationInteractor(
+ tileSpecRepository,
+ autoAddRepository,
+ qsSettingsRestoredRepository,
+ testScope.backgroundScope,
+ testDispatcher
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun reconciliationInCorrectOrder_hascurrentAutoAdded() =
+ testScope.runTest {
+ val user = 10
+ val tiles by collectLastValue(tileSpecRepository.tilesSpecs(user))
+ val autoAdd by collectLastValue(autoAddRepository.autoAddedTiles(user))
+
+ // Tile b was just auto-added, so we should re-add it in position 1
+ // Tile e was auto-added before, but the user had removed it (not in the restored set).
+ // It should not be re-added
+ val specsBeforeRestore = "a,b,c,d,e"
+ val restoredSpecs = "a,c,d,f"
+ val autoAddedBeforeRestore = "b,d"
+ val restoredAutoAdded = "d,e"
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTilesList(),
+ restoredAutoAdded.toTilesSet(),
+ user,
+ )
+
+ autoAddedBeforeRestore.toTilesSet().forEach {
+ autoAddRepository.markTileAdded(user, it)
+ }
+ tileSpecRepository.setTiles(user, specsBeforeRestore.toTilesList())
+
+ qsSettingsRestoredRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ val expectedTiles = "a,b,c,d,f"
+ assertThat(tiles).isEqualTo(expectedTiles.toTilesList())
+
+ val expectedAutoAdd = "b,d,e"
+ assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet())
+ }
+
+ companion object {
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index bce4c06..3bf59ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -172,6 +172,9 @@
@Test
fun testNotAvailableControls() {
featureEnabled = false
+
+ // Destroy previous tile
+ tile.destroy()
tile = createTile()
assertThat(tile.isAvailable).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index a0c1073..954d30ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -226,6 +226,10 @@
assertTrue(supportedTileOnlySystemUser.isAvailable());
when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
assertFalse(supportedTileOnlySystemUser.isAvailable());
+
+ destroyTile(unsupportedTile);
+ destroyTile(supportedTileAllUsers);
+ destroyTile(supportedTileOnlySystemUser);
}
@Test
@@ -250,6 +254,8 @@
mTestableLooper.processAllMessages();
assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
dockedTile.getState().icon);
+
+ destroyTile(dockedTile);
}
private void setScreensaverEnabled(boolean enabled) {
@@ -257,6 +263,11 @@
DEFAULT_USER);
}
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private DreamTile constructTileForTest(boolean dreamSupported,
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index df6993d..440270b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -216,7 +216,7 @@
public void testSecondaryString_rotationResolverDisabled_isEmpty() {
mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver,
false);
- mLockTile = new RotationLockTile(
+ RotationLockTile otherTile = new RotationLockTile(
mHost,
mUiEventLogger,
mTestableLooper.getLooper(),
@@ -232,10 +232,12 @@
new FakeSettings()
);
- mLockTile.refreshState();
+ otherTile.refreshState();
mTestableLooper.processAllMessages();
- assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ assertEquals("", otherTile.getState().secondaryLabel.toString());
+
+ destroyTile(otherTile);
}
@Test
@@ -258,6 +260,12 @@
assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
}
+
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private void enableAutoRotation() {
when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index a2aed98..59b5953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 3ae1f35..d470d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -22,13 +22,11 @@
import android.view.View
import android.widget.Spinner
import androidx.test.filters.SmallTest
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
-import com.android.systemui.mediaprojection.permission.SINGLE_APP
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -37,8 +35,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
new file mode 100644
index 0000000..091531e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.screenshot
+
+import android.media.MediaPlayer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import java.lang.IllegalStateException
+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.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+class ScreenshotSoundControllerTest : SysuiTestCase() {
+
+ private val soundProvider = mock<ScreenshotSoundProvider>()
+ private val mediaPlayer = mock<MediaPlayer>()
+ private val bgDispatcher = UnconfinedTestDispatcher()
+ private val scope = TestScope(bgDispatcher)
+ @Before
+ fun setup() {
+ whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer)
+ }
+
+ @Test
+ fun init_soundLoading() {
+ createController()
+ bgDispatcher.scheduler.runCurrent()
+
+ verify(soundProvider).getScreenshotSound()
+ }
+
+ @Test
+ fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest {
+ whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
+
+ val controller = createController()
+
+ controller.playCameraSound().await()
+ controller.releaseScreenshotSound().await()
+
+ verify(mediaPlayer, never()).start()
+ verify(mediaPlayer, never()).release()
+ }
+
+ @Test
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest {
+ val controller = createController()
+
+ controller.playCameraSound().await()
+
+ verify(mediaPlayer).start()
+ }
+
+ @Test
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
+ val controller = createController()
+
+ controller.releaseScreenshotSound().await()
+
+ verify(mediaPlayer).release()
+ }
+
+ private fun createController() =
+ ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 31c8a3d..ed731dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -118,7 +118,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -291,7 +291,7 @@
@Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock protected ShadeTransitionController mShadeTransitionController;
@Mock protected QS mQs;
- @Mock protected QSFragment mQSFragment;
+ @Mock protected QSFragmentLegacy mQSFragment;
@Mock protected ViewGroup mQsHeader;
@Mock protected ViewParent mViewParent;
@Mock protected ViewTreeObserver mViewTreeObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
index f7d2497..0c3af03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -22,9 +22,9 @@
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.QSFragment
+import com.android.systemui.qs.QSFragmentLegacy
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -40,7 +40,7 @@
@Mock private lateinit var qsFrame: View
@Mock private lateinit var stackScroller: View
@Mock private lateinit var keyguardStatusBar: View
- @Mock private lateinit var qsFragment: QSFragment
+ @Mock private lateinit var qsFragment: QSFragmentLegacy
private lateinit var qsView: ViewGroup
private lateinit var qsContainer: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index fb0d4db..8138b32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -34,7 +34,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
@@ -46,7 +45,8 @@
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
+import com.android.systemui.res.R;
import com.android.systemui.scene.SceneTestUtils;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
@@ -75,18 +75,17 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.user.domain.interactor.UserInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
-import dagger.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import dagger.Lazy;
import kotlinx.coroutines.test.TestScope;
public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -109,7 +108,7 @@
@Mock protected KeyguardBottomAreaView mQsFrame;
@Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@Mock protected QS mQs;
- @Mock protected QSFragment mQSFragment;
+ @Mock protected QSFragmentLegacy mQSFragment;
@Mock protected Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
@Mock protected NotificationPanelViewController mNotificationPanelViewController;
@Mock protected NotificationPanelView mPanelView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index d018cbb..2be1c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -35,6 +34,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -54,6 +54,7 @@
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -153,6 +154,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
underTest =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index f05436f..50ce265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -117,6 +118,8 @@
PendingIntent mPendingIntent;
@Mock
UserTracker mUserTracker;
+ @Mock
+ DeviceProvisionedController mDeviceProvisionedController;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -141,7 +144,8 @@
mFlags,
mKeyguardNotificationVisibilityProvider,
mUiEventLoggerFake,
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
@@ -694,6 +698,25 @@
}
@Test
+ public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
+
+ assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+ .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED);
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED");
+ }
+
+ @Test
public void testShouldNotFullScreen_willHun() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d0256..7dcbd80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -73,14 +73,14 @@
controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder)
channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
- channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT)
+ channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE)
channelDefault = NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME)
- `when`(mockNoMan.getNotificationChannelGroupsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean()))
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(ParceledListSlice(listOf(group)))
`when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
@@ -89,11 +89,13 @@
@Test
fun testPrepareDialogForApp_noExtraChannels() {
+ channel1.group = group.id
+ channel2.group = group.id
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals(2, controller.paddedChannels.size)
+ assertEquals(2, controller.channelList.size)
}
@Test
@@ -101,39 +103,41 @@
group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channelDefault), appIcon, clickListener)
+ channelDefault, appIcon, clickListener)
assertEquals("No channels should be shown when there is only the miscellaneous channel",
- 0, controller.paddedChannels.size)
+ 0, controller.channelList.size)
}
@Test
- fun testPrepareDialogForApp_noProvidedChannels_noException() {
- group.channels = listOf()
-
- controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(), appIcon, clickListener)
- }
-
- @Test
- fun testPrepareDialogForApp_retrievesUpTo4Channels() {
+ fun testPrepareDialogForApp_AddsAllChannelsAllGroups() {
+ val group2 = NotificationChannelGroup("two", "group two")
val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT)
+ channel3.group = group2.id
val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT)
+ channel4.group = group.id
+ val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT)
+ channel5.group = group.id
- group.channels = listOf(channel1, channel2, channel3, channel4)
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(ParceledListSlice(listOf(group, group2)))
+
+ group.channels = listOf(channel1, channel2, channel4, channel5)
+ group2.channels = listOf(channel3)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
- 4, controller.paddedChannels.size)
+ assertEquals("ChannelEditorDialog should show all channels",
+ 5, controller.channelList.size)
}
@Test
fun testApply_demoteChannel() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
// propose an adjustment of channel1
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -145,14 +149,33 @@
// Channel 2 shouldn't have changed
assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_NONE, channel2.importance)
+ }
+
+ @Test
+ fun testApply_promoteChannel() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ channel1, appIcon, clickListener)
+
+ // propose an adjustment of channel1
+ controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT)
+
+ controller.apply()
+
+ assertEquals("Proposed edits should take effect after apply",
IMPORTANCE_DEFAULT, channel2.importance)
+
+ // Channel 1 shouldn't have changed
+ assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_DEFAULT, channel1.importance)
}
@Test
fun testApply_demoteApp() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(false)
controller.apply()
@@ -168,7 +191,7 @@
.thenReturn(false)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(true)
controller.apply()
@@ -181,7 +204,7 @@
// GIVEN editor dialog
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN user taps settings
// Pass in any old view, it should never actually be used
@@ -197,7 +220,7 @@
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -214,7 +237,7 @@
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -236,7 +259,6 @@
const val TEST_PACKAGE_NAME = "test_package"
const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME
const val TEST_UID = 1
- const val MULTIPLE_CHANNEL_COUNT = 2
const val TEST_CHANNEL = "test_channel"
const val TEST_CHANNEL_NAME = "Test Channel Name"
const val TEST_CHANNEL2 = "test_channel2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ac8b42a..e373143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -481,33 +481,6 @@
}
@Test
- public void testGetNumUniqueChildren_defaultChannel() throws Exception {
- ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
-
- assertEquals(1, groupRow.getNumUniqueChannels());
- }
-
- @Test
- public void testGetNumUniqueChildren_multiChannel() throws Exception {
- ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
-
- List<ExpandableNotificationRow> childRows =
- group.getChildrenContainer().getAttachedChildren();
- // Give each child a unique channel id/name.
- int i = 0;
- for (ExpandableNotificationRow childRow : childRows) {
- modifyRanking(childRow.getEntry())
- .setChannel(
- new NotificationChannel(
- "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
- .build();
- i++;
- }
-
- assertEquals(3, group.getNumUniqueChannels());
- }
-
- @Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9e0f83c..0f1e63f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -462,7 +462,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -496,7 +495,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -528,7 +526,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f0b4dd4..b59385c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -92,7 +92,6 @@
private static final String TEST_PACKAGE_NAME = "test_package";
private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
private static final int TEST_UID = 1;
- private static final int MULTIPLE_CHANNEL_COUNT = 2;
private static final String TEST_CHANNEL = "test_channel";
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
@@ -100,8 +99,6 @@
private NotificationInfo mNotificationInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
@@ -162,11 +159,9 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
@@ -185,7 +180,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -212,7 +206,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -235,7 +228,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -267,7 +259,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
null,
@@ -291,7 +282,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -320,7 +310,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -344,7 +333,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -367,7 +355,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -394,7 +381,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -417,7 +403,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -441,7 +426,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -470,7 +454,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -494,7 +477,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -519,7 +501,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -536,7 +517,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> { },
null,
@@ -551,86 +531,6 @@
}
@Test
- public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- (View v, NotificationChannel c, int appUid) -> {
- assertEquals(null, c);
- latch.countDown();
- },
- null,
- mUiEventLogger,
- true,
- true,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
-
- mNotificationInfo.findViewById(R.id.info).performClick();
- // Verify that listener was triggered.
- assertEquals(0, latch.getCount());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
- throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- final TextView channelNameView =
- mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(GONE, channelNameView.getVisibility());
- }
-
- @Test
- @UiThreadTest
- public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- assertEquals(GONE, mNotificationInfo.findViewById(
- R.id.interruptiveness_settings).getVisibility());
- assertEquals(VISIBLE, mNotificationInfo.findViewById(
- R.id.non_configurable_multichannel_text).getVisibility());
- }
-
- @Test
public void testBindNotification_whenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
@@ -639,7 +539,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -683,7 +582,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -727,7 +625,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -755,7 +652,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -778,7 +674,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -803,7 +698,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -825,7 +719,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -847,7 +740,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -869,7 +761,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -893,7 +784,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -918,7 +808,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -946,7 +835,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -974,7 +862,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1003,7 +890,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1031,7 +917,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1067,7 +952,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1096,7 +980,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1138,7 +1021,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1176,7 +1058,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1209,7 +1090,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1246,7 +1126,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1285,7 +1164,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1317,7 +1195,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1356,7 +1233,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1386,7 +1262,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1418,7 +1293,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1454,7 +1328,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1488,7 +1361,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1522,7 +1394,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1549,7 +1420,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1562,21 +1432,4 @@
assertFalse(mNotificationInfo.willBeRemoved());
}
-
- private Set<NotificationChannel> createMultipleChannelSet(int howMany) {
- Set<NotificationChannel> multiChannelSet = new HashSet<>();
- for (int i = 0; i < howMany; i++) {
- if (i == 0) {
- multiChannelSet.add(mNotificationChannel);
- continue;
- }
-
- NotificationChannel channel = new NotificationChannel(
- TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW);
-
- multiChannelSet.add(channel);
- }
-
- return multiChannelSet;
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index e42ce27..ccedd36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -83,8 +83,6 @@
private PartialConversationInfo mInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
@@ -144,11 +142,9 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setContentTitle(new SpannableString("title"))
.build();
@@ -166,7 +162,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -185,7 +180,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -202,7 +196,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -228,7 +221,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
true,
@@ -247,7 +239,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -271,7 +262,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -294,7 +284,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -311,7 +300,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -330,7 +318,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5c3dde5..0b171b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -364,7 +364,8 @@
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1169,7 +1170,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(
contentResolver,
powerManager,
@@ -1183,7 +1185,8 @@
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker
+ userTracker,
+ deviceProvisionedController
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 99e4030..b54fbd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -90,6 +90,8 @@
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
+ override val isAnySimSecure = MutableStateFlow(false)
+
fun setSubscriptions(subs: List<SubscriptionModel>) {
_subscriptions.value = subs
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index d005972..4d4f33b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -135,6 +135,7 @@
FakeAirplaneModeRepository(),
wifiRepository,
mock(),
+ mock(),
)
demoRepo =
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 6f9764a..9148c75 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
@@ -37,6 +37,8 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.R
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
@@ -104,6 +106,7 @@
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var summaryLogger: TableLogBuffer
@Mock private lateinit var logBufferFactory: TableLogBufferFactory
+ @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
private val mobileMappings = FakeMobileMappingsProxy()
private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
@@ -214,6 +217,7 @@
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor,
)
testScope.runCurrent()
@@ -1048,6 +1052,7 @@
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor
)
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1103,7 +1108,6 @@
@Test
fun carrierConfig_initialValueIsFetched() =
testScope.runTest {
-
// Value starts out false
assertThat(underTest.defaultDataSubRatConfig.value.showAtLeast3G).isFalse()
@@ -1151,6 +1155,26 @@
assertThat(latest).isEqualTo(null)
}
+ @Test
+ fun anySimSecure_propagatesStateFromKeyguardUpdateMonitor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnySimSecure)
+ assertThat(latest).isFalse()
+
+ val updateMonitorCallback = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isTrue()
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(false)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isFalse()
+ }
+
private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
runCurrent()
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index bbc49c8..af941d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -34,7 +34,6 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
@@ -45,6 +44,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -1120,6 +1120,7 @@
),
uiEventLogger = uiEventLogger,
featureFlags = featureFlags,
+ userRestrictionChecker = mock(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 2433e12..a8db368 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -267,6 +267,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 8c88f95..6932f5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
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.ExperimentalCoroutinesApi
@@ -176,6 +177,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
),
guestUserInteractor = guestUserInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 94ed608..e59e475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -15,104 +15,119 @@
*/
package com.android.systemui.wmshell
-import android.content.ContentResolver
import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.core.content.edit
import androidx.test.filters.SmallTest
import com.android.systemui.model.SysUiStateTest
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION
import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BubbleEducationControllerTest : SysUiStateTest() {
- private val sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor::class.java)
- private val sharedPrefs = Mockito.mock(SharedPreferences::class.java)
- private val context = Mockito.mock(Context::class.java)
+
+ private lateinit var sharedPrefs: SharedPreferences
private lateinit var sut: BubbleEducationController
@Before
fun setUp() {
- Mockito.`when`(context.packageName).thenReturn("packageName")
- Mockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs)
- Mockito.`when`(context.contentResolver)
- .thenReturn(Mockito.mock(ContentResolver::class.java))
- Mockito.`when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
- sut = BubbleEducationController(context)
+ sharedPrefs = mContext.getSharedPreferences(mContext.packageName, Context.MODE_PRIVATE)
+ sharedPrefs.edit {
+ remove(PREF_STACK_EDUCATION)
+ remove(PREF_MANAGED_EDUCATION)
+ }
+ sut = BubbleEducationController(mContext)
}
@Test
fun testSeenStackEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
assertEquals(sut.hasSeenStackEducation, true)
- Mockito.verify(sharedPrefs).getBoolean(PREF_STACK_EDUCATION, false)
}
@Test
fun testSeenStackEducation_write() {
sut.hasSeenStackEducation = true
- Mockito.verify(sharedPrefsEditor).putBoolean(PREF_STACK_EDUCATION, true)
+ assertThat(sharedPrefs.getBoolean(PREF_STACK_EDUCATION, false)).isTrue()
}
@Test
fun testSeenManageEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
assertEquals(sut.hasSeenManageEducation, true)
- Mockito.verify(sharedPrefs).getBoolean(PREF_MANAGED_EDUCATION, false)
}
@Test
fun testSeenManageEducation_write() {
sut.hasSeenManageEducation = true
- Mockito.verify(sharedPrefsEditor).putBoolean(PREF_MANAGED_EDUCATION, true)
+ assertThat(sharedPrefs.getBoolean(PREF_MANAGED_EDUCATION, false)).isTrue()
}
@Test
fun testShouldShowStackEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowStackEducation(null), false)
+ var bubble = createFakeBubble(isConversational = false)
// When bubble is not conversation
- Mockito.`when`(bubble.isConversation).thenReturn(false)
assertEquals(sut.shouldShowStackEducation(bubble), false)
// When bubble is conversation and has seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
assertEquals(sut.shouldShowStackEducation(bubble), false)
// When bubble is conversation and has not seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ sharedPrefs.edit { remove(PREF_STACK_EDUCATION) }
assertEquals(sut.shouldShowStackEducation(bubble), true)
}
@Test
fun testShouldShowManageEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowManageEducation(null), false)
+ var bubble = createFakeBubble(isConversational = false)
// When bubble is not conversation
- Mockito.`when`(bubble.isConversation).thenReturn(false)
assertEquals(sut.shouldShowManageEducation(bubble), false)
// When bubble is conversation and has seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
assertEquals(sut.shouldShowManageEducation(bubble), false)
// When bubble is conversation and has not seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ sharedPrefs.edit { remove(PREF_MANAGED_EDUCATION) }
assertEquals(sut.shouldShowManageEducation(bubble), true)
}
+
+ private fun createFakeBubble(isConversational: Boolean): Bubble {
+ return if (isConversational) {
+ val shortcutInfo = ShortcutInfo.Builder(mContext, "fakeId").build()
+ Bubble(
+ "key",
+ shortcutInfo,
+ /* desiredHeight= */ 6,
+ Resources.ID_NULL,
+ "title",
+ /* taskId= */ 0,
+ "locus",
+ /* isDismissable= */ true,
+ directExecutor()
+ ) {}
+ } else {
+ val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName)
+ Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d8511e8..65b8b55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,7 @@
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -391,7 +392,8 @@
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mock(UserTracker.class)
+ mock(UserTracker.class),
+ mock(DeviceProvisionedController.class)
);
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 4e14bbf6..0df235d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +49,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(contentResolver,
powerManager,
ambientDisplayConfiguration,
@@ -61,7 +63,8 @@
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker);
+ userTracker,
+ deviceProvisionedController);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 21a5eb7..28557d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -17,6 +17,7 @@
package com.android.systemui.broadcast
import android.content.BroadcastReceiver
+import android.content.BroadcastReceiver.PendingResult
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@@ -28,7 +29,6 @@
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
-import java.lang.IllegalStateException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
@@ -96,8 +96,14 @@
/**
* Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter]
* that matches the intent.
+ *
+ * A non-null [pendingResult] can be used to pass the sending user.
*/
- fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) {
+ fun sendIntentToMatchingReceiversOnly(
+ context: Context,
+ intent: Intent,
+ pendingResult: PendingResult? = null
+ ) {
receivers.forEach {
if (
it.filter.match(
@@ -107,6 +113,9 @@
/* logTag= */ "FakeBroadcastDispatcher",
) > 0
) {
+ if (pendingResult != null) {
+ it.receiver.pendingResult = pendingResult
+ }
it.receiver.onReceive(context, intent)
}
}
@@ -130,4 +139,19 @@
val receiver: BroadcastReceiver,
val filter: IntentFilter,
)
+
+ companion object {
+ fun fakePendingResultForUser(userId: Int) =
+ PendingResult(
+ /* resultCode = */ 0,
+ /* resultData = */ "",
+ /* resultExtras = */ null,
+ /* type = */ PendingResult.TYPE_REGISTERED,
+ /* ordered = */ false,
+ /* sticky = */ false,
+ /* token = */ null,
+ userId,
+ /* flags = */ 0,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index ceab8e9..945aaed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -50,6 +50,7 @@
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.utils.UserRestrictionChecker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock
@@ -137,6 +138,7 @@
refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
guestUserInteractor = mock(GuestUserInteractor::class.java),
uiEventLogger = mock(UiEventLogger::class.java),
+ userRestrictionChecker = mock(UserRestrictionChecker::class.java),
)
return WithDependencies(
trustRepository = trustRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
index 9ea079f..57ad2828 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
@@ -16,15 +16,16 @@
package com.android.systemui.qs.pipeline.data.repository
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
class FakeAutoAddRepository : AutoAddRepository {
private val autoAddedTilesPerUser = mutableMapOf<Int, MutableStateFlow<Set<TileSpec>>>()
- override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+ override suspend fun autoAddedTiles(userId: Int): StateFlow<Set<TileSpec>> {
return getFlow(userId)
}
@@ -39,4 +40,8 @@
private fun getFlow(userId: Int): MutableStateFlow<Set<TileSpec>> =
autoAddedTilesPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) }
+
+ override suspend fun reconcileRestore(restoreData: RestoreData) {
+ with(getFlow(restoreData.userId)) { value = value + restoreData.restoredAutoAddedTiles }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
new file mode 100644
index 0000000..e0c2154
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
@@ -0,0 +1,16 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeQSSettingsRestoredRepository : QSSettingsRestoredRepository {
+ private val _restoreData = MutableSharedFlow<RestoreData>()
+
+ override val restoreData: Flow<RestoreData>
+ get() = _restoreData
+
+ suspend fun onDataRestored(restoreData: RestoreData) {
+ _restoreData.emit(restoreData)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index aa8dbe1..ae4cf3a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.pipeline.data.repository
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.shared.TileSpec
import kotlinx.coroutines.flow.Flow
@@ -26,7 +27,7 @@
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
- override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
return getFlow(userId).asStateFlow()
}
@@ -57,4 +58,13 @@
private fun getFlow(userId: Int): MutableStateFlow<List<TileSpec>> =
tilesPerUser.getOrPut(userId) { MutableStateFlow(emptyList()) }
+
+ override suspend fun reconcileRestore(
+ restoreData: RestoreData,
+ currentAutoAdded: Set<TileSpec>
+ ) {
+ with(getFlow(restoreData.userId)) {
+ value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
+ }
+ }
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 0480c22..11189cf 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -25,5 +25,12 @@
name: "send_a11y_events_based_on_state"
namespace: "accessibility"
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
-bug: "295575684"
-}
\ No newline at end of file
+ bug: "295575684"
+}
+
+flag {
+ name: "add_window_token_without_lock"
+ namespace: "accessibility"
+ description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock"
+ bug: "297972548"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4..fa73cff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1506,11 +1506,17 @@
}
}
- public void onAdded() {
+ /**
+ * Called when the connection is first created. Add a window token for all known displays.
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ */
+ void addWindowTokensForAllDisplays() {
final Display[] displays = mDisplayManager.getDisplays();
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
- onDisplayAdded(displayId);
+ addWindowTokenForDisplay(displayId);
}
}
@@ -1518,9 +1524,13 @@
* Called whenever a logical display has been added to the system. Add a window token for adding
* an accessibility overlay.
*
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ *
* @param displayId The id of the logical display that was added.
*/
- public void onDisplayAdded(int displayId) {
+ void addWindowTokenForDisplay(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 93ba362..60d4ee6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -154,6 +154,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
+import com.android.internal.util.Preconditions;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -4500,6 +4501,20 @@
private int mSystemUiUid = 0;
AccessibilityDisplayListener(Context context, Handler handler) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ // Avoid concerns about one thread adding displays while another thread removes
+ // them by ensuring the looper is the main looper and the DisplayListener
+ // callbacks are always executed on the one main thread.
+ final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+ final String errorMessage =
+ "AccessibilityDisplayListener must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
+
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -4541,11 +4556,21 @@
@Override
public void onDisplayAdded(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayAdded must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
final Display display = mDisplayManager.getDisplay(displayId);
if (!isValidDisplay(display)) {
return;
}
+ final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
mDisplaysList.add(display);
mA11yOverlayLayers.put(
@@ -4554,21 +4579,42 @@
mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
- if (displayId != Display.DEFAULT_DISPLAY) {
- final List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection boundClient = services.get(i);
- boundClient.onDisplayAdded(displayId);
+ if (Flags.addWindowTokenWithoutLock()) {
+ services = new ArrayList<>(userState.mBoundServices);
+ } else {
+ services = userState.mBoundServices;
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
}
}
updateMagnificationLocked(userState);
updateWindowsForAccessibilityCallbackLocked(userState);
notifyClearAccessibilityCacheLocked();
}
+ if (Flags.addWindowTokenWithoutLock()) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
+ }
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayRemoved must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
synchronized (mLock) {
if (!removeDisplayFromList(displayId)) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 9e70073..7a2a602 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -44,7 +44,6 @@
import android.view.Display;
import android.view.MotionEvent;
-
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -169,6 +168,10 @@
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
+ AccessibilityUserState userState = mUserStateWeakReference.get();
+ if (userState != null && Flags.addWindowTokenWithoutLock()) {
+ addWindowTokensForAllDisplays();
+ }
synchronized (mLock) {
if (mService != service) {
if (mService != null) {
@@ -184,7 +187,6 @@
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
- AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this);
mSystemSupport.onClientChangeLocked(false);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index ab6cc71..693526a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -224,7 +224,9 @@
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
if (!mBoundServices.contains(serviceConnection)) {
- serviceConnection.onAdded();
+ if (!Flags.addWindowTokenWithoutLock()) {
+ serviceConnection.addWindowTokensForAllDisplays();
+ }
mBoundServices.add(serviceConnection);
mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 208acdf..53c629a 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,9 +25,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -35,6 +37,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.utils.Slogf;
import com.android.server.wm.WindowManagerInternal;
@@ -98,46 +101,47 @@
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
- synchronized (mLock) {
- if (mUiAutomationService != null) {
- throw new IllegalStateException(
- "UiAutomationService " + mUiAutomationService.mServiceInterface
- + "already registered!");
- }
-
- try {
- owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
- re);
- return;
- }
-
- mUiAutomationFlags = flags;
- mSystemSupport = systemSupport;
- // Ignore registering UiAutomation if it is not allowed to use the accessibility
- // subsystem.
- if (!useAccessibility()) {
- return;
- }
- mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
- mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
- systemActionPerformer, awm);
- mUiAutomationServiceOwner = owner;
- mUiAutomationService.mServiceInterface = serviceClient;
- try {
- mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
- 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed registering death link: " + re);
- destroyUiAutomationService();
- return;
- }
-
- mUiAutomationService.onAdded();
-
- mUiAutomationService.connectServiceUnknownThread();
+ if (mUiAutomationService != null) {
+ throw new IllegalStateException(
+ "UiAutomationService " + mUiAutomationService.mServiceInterface
+ + "already registered!");
}
+
+ try {
+ owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
+ re);
+ return;
+ }
+
+ mUiAutomationFlags = flags;
+ mSystemSupport = systemSupport;
+ // Ignore registering UiAutomation if it is not allowed to use the accessibility
+ // subsystem.
+ if (!useAccessibility()) {
+ return;
+ }
+ mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
+ mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
+ systemActionPerformer, awm);
+ mUiAutomationServiceOwner = owner;
+ mUiAutomationService.mServiceInterface = serviceClient;
+ try {
+ mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
+ 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed registering death link: " + re);
+ destroyUiAutomationService();
+ return;
+ }
+
+ if (!Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
+ }
+ // UiAutomationService#connectServiceUnknownThread posts to a handler
+ // so this call should return immediately.
+ mUiAutomationService.connectServiceUnknownThread();
}
void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
@@ -253,6 +257,13 @@
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
securityPolicy, systemSupport, trace, windowManagerInternal,
systemActionPerformer, awm);
+ final boolean isMainHandler = mainHandler.getLooper() == Looper.getMainLooper();
+ final String errorMessage = "UiAutomationService must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
mMainHandler = mainHandler;
setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
}
@@ -274,6 +285,9 @@
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
+ }
if (mTrace.isA11yTracingEnabledForTypes(
AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
@@ -286,7 +300,7 @@
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
} catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error initialized connection", re);
+ Slog.w(LOG_TAG, "Error initializing connection", re);
destroyUiAutomationService();
}
});
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ba45339..c791498 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -155,6 +155,7 @@
"debug.cdm.cdmservice.removal_time_window";
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
+ private static final int MAX_CN_LENGTH = 500;
private final ActivityManager mActivityManager;
private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
@@ -757,6 +758,9 @@
String callingPackage = component.getPackageName();
checkCanCallNotificationApi(callingPackage);
// TODO: check userId.
+ if (component.flattenToString().length() > MAX_CN_LENGTH) {
+ throw new IllegalArgumentException("Component name is too long.");
+ }
final long identity = Binder.clearCallingIdentity();
try {
return PendingIntent.getActivityAsUser(getContext(),
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index e8839a2..720687e 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -562,7 +562,8 @@
private byte[] constructToken(D2DHandshakeContext.Role role, byte[] authValue)
throws GeneralSecurityException {
MessageDigest hash = MessageDigest.getInstance("SHA-256");
- byte[] roleUtf8 = role.name().getBytes(StandardCharsets.UTF_8);
+ String roleName = role == Role.INITIATOR ? "Initiator" : "Responder";
+ byte[] roleUtf8 = roleName.getBytes(StandardCharsets.UTF_8);
int tokenLength = roleUtf8.length + authValue.length;
return hash.digest(ByteBuffer.allocate(tokenLength)
.put(roleUtf8)
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9c2694..6521fab 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -194,6 +194,7 @@
"notification_flags_lib",
"camera_platform_flags_core_java_lib",
"biometrics_flags_lib",
+ "am_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 25ca509c..8cc2665 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -265,17 +265,17 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- mStorageManagerService.onUnlockUser(user.getUserIdentifier());
+ mStorageManagerService.onUserUnlocking(user.getUserIdentifier());
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
- mStorageManagerService.onCleanupUser(user.getUserIdentifier());
+ mStorageManagerService.onUserStopped(user.getUserIdentifier());
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
- mStorageManagerService.onStopUser(user.getUserIdentifier());
+ mStorageManagerService.onUserStopping(user.getUserIdentifier());
}
@Override
@@ -1163,8 +1163,8 @@
}
}
- private void onUnlockUser(int userId) {
- Slog.d(TAG, "onUnlockUser " + userId);
+ private void onUserUnlocking(int userId) {
+ Slog.d(TAG, "onUserUnlocking " + userId);
if (userId != UserHandle.USER_SYSTEM) {
// Check if this user shares media with another user
@@ -1227,8 +1227,8 @@
}
}
- private void onCleanupUser(int userId) {
- Slog.d(TAG, "onCleanupUser " + userId);
+ private void onUserStopped(int userId) {
+ Slog.d(TAG, "onUserStopped " + userId);
try {
mVold.onUserStopped(userId);
@@ -1242,8 +1242,8 @@
}
}
- private void onStopUser(int userId) {
- Slog.i(TAG, "onStopUser " + userId);
+ private void onUserStopping(int userId) {
+ Slog.i(TAG, "onUserStopping " + userId);
try {
mStorageSessionController.onUserStopping(userId);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index c20f0aa..9716cf6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
new file mode 100644
index 0000000..af1200e
--- /dev/null
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+ name: "am_flags",
+ package: "com.android.server.am",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "am_flags_lib",
+ aconfig_declarations: "am_flags",
+}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 128bbdf..907069d 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -82,6 +82,7 @@
import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
import com.android.server.pm.UserManagerInternal;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.Arrays;
@@ -571,8 +572,18 @@
builder = new BatteryUsageStatsQuery.Builder()
.includeProcessStateData()
.aggregateSnapshots(lastUidBatteryUsageStartTs, curStart);
- updateBatteryUsageStatsOnceInternal(0, buf, builder, userIds, batteryStatsInternal);
+ final BatteryUsageStats statsCommit =
+ updateBatteryUsageStatsOnceInternal(0,
+ buf,
+ builder,
+ userIds,
+ batteryStatsInternal);
curDuration += curStart - lastUidBatteryUsageStartTs;
+ try {
+ statsCommit.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat");
+ }
}
if (needUpdateUidBatteryUsageInWindow && curDuration >= windowSize) {
// If we do have long enough data for the window, save it.
@@ -648,8 +659,14 @@
}
}
}
+ try {
+ stats.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat");
+ }
}
+ // The BatteryUsageStats object MUST BE CLOSED when finished using
private BatteryUsageStats updateBatteryUsageStatsOnceInternal(long expectedDuration,
SparseArray<BatteryUsage> buf, BatteryUsageStatsQuery.Builder builder,
ArraySet<UserHandle> userIds, BatteryStatsInternal batteryStatsInternal) {
@@ -662,7 +679,16 @@
// Shouldn't happen unless in test.
return null;
}
+ // We need the first stat in the list, so we should
+ // close out the others.
final BatteryUsageStats stats = statsList.get(0);
+ for (int i = 1; i < statsList.size(); i++) {
+ try {
+ statsList.get(i).close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List");
+ }
+ }
final List<UidBatteryConsumer> uidConsumers = stats.getUidBatteryConsumers();
if (uidConsumers != null) {
final long start = stats.getStatsStartTimestamp();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 5fa0ffa..6005b64 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1442,7 +1442,6 @@
uidRec.setFrozen(false);
postUidFrozenMessage(uidRec.getUid(), false);
}
- reportProcessFreezableChangedLocked(app);
opt.setFreezerOverride(false);
if (pid == 0 || !opt.isFrozen()) {
@@ -1481,6 +1480,7 @@
if (processKilled) {
return;
}
+ reportProcessFreezableChangedLocked(app);
long freezeTime = opt.getFreezeUnfreezeTime();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index e84fed7..4b622f5 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -173,6 +173,16 @@
TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU,
TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class,
TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
+
+ // Register all text aconfig flags.
+ for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) {
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
+ TextFlags.NAMESPACE,
+ flag,
+ TextFlags.getKeyForFlag(flag),
+ boolean.class,
+ false)); // All aconfig flags are false by default.
+ }
// add other device configs here...
}
private static volatile boolean sDeviceConfigContextEntriesLoaded = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3d11c68..0615ecf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3087,6 +3087,8 @@
if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
+ // Ensure that the mCrashing flag is cleared, since this is a restart
+ proc.resetCrashingOnRestart();
} else if (old != null) {
if (old.isKilled()) {
// The old process has been killed, we probably haven't had
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cfbb5a5..d8a2695 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -618,6 +618,10 @@
mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
+ void resetCrashingOnRestart() {
+ mErrorState.setCrashing(false);
+ }
+
@GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord getUidRecord() {
return mUidRecord;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
new file mode 100644
index 0000000..b03cc62
--- /dev/null
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.am"
+
+flag {
+ name: "oomadjuster_correctness_rewrite"
+ namespace: "android_platform_power_optimization"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "298055811"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 99a5398..debf828 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessUtils;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.Plog;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 13ee47e..40b2f5a 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -43,14 +43,16 @@
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
+ new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags,
+ displayToken, info);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- HdrClamper hdrClamper, DisplayManagerFlags flags) {
+ HdrClamper hdrClamper, DisplayManagerFlags flags, IBinder displayToken,
+ DisplayDeviceInfo info) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
mHdrClamper = hdrClamper;
@@ -60,10 +62,7 @@
mNormalBrightnessModeController.resetNbmData(
displayDeviceConfig.getLuxThrottlingData());
}
- if (mUseHdrClamper) {
- mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
- }
-
+ updateHdrClamper(info, displayToken, displayDeviceConfig);
}
void dump(PrintWriter pw) {
@@ -101,13 +100,12 @@
displayDeviceConfig::getHdrBrightnessFromSdr);
}
);
- if (mUseHdrClamper) {
- mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
- }
+ updateHdrClamper(info, token, displayDeviceConfig);
}
void stop() {
mHbmController.stop();
+ mHdrClamper.stop();
}
void setAutoBrightnessEnabled(int state) {
@@ -151,6 +149,18 @@
return mHbmController.getTransitionPoint();
}
+ private void updateHdrClamper(DisplayDeviceInfo info, IBinder token,
+ DisplayDeviceConfig displayDeviceConfig) {
+ if (mUseHdrClamper) {
+ DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ displayDeviceConfig.getHighBrightnessModeData();
+ float minimumHdrPercentOfScreen =
+ hbmData == null ? -1f : hbmData.minimumHdrPercentOfScreen;
+ mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData(), info.width,
+ info.height, minimumHdrPercentOfScreen, token);
+ }
+ }
+
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
if (mUseNbmController) {
boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 8642fb8..098cb87 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
import android.util.Slog;
@@ -205,6 +206,24 @@
*/
public Runnable requestDisplayStateLocked(int state, float brightnessState,
float sdrBrightnessState) {
+ return requestDisplayStateLocked(state, brightnessState, sdrBrightnessState, null);
+ }
+
+ /**
+ * Sets the display state, if supported.
+ *
+ * @param state The new display state.
+ * @param brightnessState The new display brightnessState.
+ * @param sdrBrightnessState The new display brightnessState for SDR layers.
+ * @param displayOffloadSession {@link DisplayOffloadSession} associated with current device.
+ * @return A runnable containing work to be deferred until after we have exited the critical
+ * section, or null if none.
+ */
+ public Runnable requestDisplayStateLocked(
+ int state,
+ float brightnessState,
+ float sdrBrightnessState,
+ @Nullable DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9ef84cb..e942c17 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1772,7 +1772,7 @@
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(mInjector.getLocalDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo));
+ mHandler, mDisplayDeviceRepo, mFlags));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
@@ -2093,8 +2093,11 @@
// Only send a request for display state if display state has already been initialized.
if (state != Display.STATE_UNKNOWN) {
final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
- return device.requestDisplayStateLocked(state, brightnessPair.brightness,
- brightnessPair.sdrBrightness);
+ return device.requestDisplayStateLocked(
+ state,
+ brightnessPair.brightness,
+ brightnessPair.sdrBrightness,
+ display.getDisplayOffloadSessionLocked());
}
}
return null;
@@ -3183,9 +3186,10 @@
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler,
- DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ flags);
}
long getDefaultDisplayDelayTimeout() {
@@ -4806,6 +4810,49 @@
}
return displayGroupIds;
}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloadSession registerDisplayOffloader(
+ int displayId, @NonNull DisplayManagerInternal.DisplayOffloader displayOffloader) {
+ if (!mFlags.isDisplayOffloadEnabled()) {
+ return null;
+ }
+ synchronized (mSyncRoot) {
+ LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (logicalDisplay == null) {
+ Slog.w(TAG, "registering DisplayOffloader: LogicalDisplay for displayId="
+ + displayId + " is not found. No Op.");
+ return null;
+ }
+
+ DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(logicalDisplay.getDisplayIdLocked());
+ if (displayPowerController == null) {
+ Slog.w(TAG,
+ "setting doze state override: DisplayPowerController for displayId="
+ + displayId + " is unavailable. No Op.");
+ return null;
+ }
+
+ DisplayOffloadSession session =
+ new DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {
+ synchronized (mSyncRoot) {
+ displayPowerController.overrideDozeScreenState(displayState);
+ }
+ }
+
+ @Override
+ public DisplayOffloader getDisplayOffloader() {
+ return displayOffloader;
+ }
+ };
+ logicalDisplay.setDisplayOffloadSessionLocked(session);
+ displayPowerController.setDisplayOffloadSession(session);
+ return session;
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 83f4df9..ce98559 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,8 @@
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.metrics.LogMaker;
@@ -588,8 +590,9 @@
new SparseArray<>();
private boolean mBootCompleted;
-
private final DisplayManagerFlags mFlags;
+ private int mDozeStateOverride = Display.STATE_UNKNOWN;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
/**
* Creates the display power controller.
@@ -684,7 +687,9 @@
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+ mDisplayDevice.getDisplayTokenLocked(),
+ mDisplayDevice.getDisplayDeviceInfoLocked());
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -957,6 +962,23 @@
}
@Override
+ public void overrideDozeScreenState(int displayState) {
+ synchronized (mLock) {
+ if (mDisplayOffloadSession == null ||
+ !DisplayOffloadSession.isSupportedOffloadState(displayState)) {
+ return;
+ }
+ mDozeStateOverride = displayState;
+ sendUpdatePowerState();
+ }
+ }
+
+ @Override
+ public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -1518,6 +1540,7 @@
} else {
state = Display.STATE_DOZE;
}
+ state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
if (!mAllowAutoBrightnessWhileDozingConfig) {
brightnessState = mPowerRequest.dozeScreenBrightness;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
@@ -1937,6 +1960,7 @@
// We want to scale HDR brightness level with the SDR level, we also need to restore
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
}
final float currentBrightness = mPowerState.getScreenBrightness();
@@ -3001,6 +3025,7 @@
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
+ pw.println(" mDozeStateOverride=" + mDozeStateOverride);
pw.println();
pw.println("Display Power Controller Locked State:");
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index b0d293a..1652871 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -31,6 +31,8 @@
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.metrics.LogMaker;
@@ -470,9 +472,10 @@
new SparseArray();
private boolean mBootCompleted;
-
private final DisplayManagerFlags mFlags;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
/**
* Creates the display power controller.
*/
@@ -549,7 +552,9 @@
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+ mDisplayDevice.getDisplayTokenLocked(),
+ mDisplayDevice.getDisplayDeviceInfoLocked());
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
@@ -588,21 +593,24 @@
if (mDisplayId == Display.DEFAULT_DISPLAY) {
mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
- boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
- @Override
- public void onReduceBrightColorsActivationChanged(boolean activated,
- boolean userInitiated) {
- applyReduceBrightColorsSplineAdjustment();
+ if (mCdsi != null) {
+ boolean active = mCdsi.setReduceBrightColorsListener(
+ new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated,
+ boolean userInitiated) {
+ applyReduceBrightColorsSplineAdjustment();
- }
+ }
- @Override
- public void onReduceBrightColorsStrengthChanged(int strength) {
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
applyReduceBrightColorsSplineAdjustment();
}
- });
- if (active) {
- applyReduceBrightColorsSplineAdjustment();
}
} else {
mCdsi = null;
@@ -760,6 +768,24 @@
}
@Override
+ public void overrideDozeScreenState(int displayState) {
+ mHandler.postAtTime(() -> {
+ if (mDisplayOffloadSession == null
+ || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
+ || displayState == Display.STATE_UNKNOWN)) {
+ return;
+ }
+ mDisplayStateController.overrideDozeScreenState(displayState);
+ sendUpdatePowerState();
+ }, mClock.uptimeMillis());
+ }
+
+ @Override
+ public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -1540,6 +1566,7 @@
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
}
final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1871,15 +1898,12 @@
private HighBrightnessModeController createHbmControllerLocked(
HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) {
- final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
- final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
- final IBinder displayToken =
- mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
- final String displayUniqueId =
- mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig();
+ final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked();
+ final String displayUniqueId = mDisplayDevice.getUniqueId();
final DisplayDeviceConfig.HighBrightnessModeData hbmData =
ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
- final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked();
return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height,
displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) ->
@@ -3025,9 +3049,9 @@
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
return new BrightnessRangeController(hbmController,
- modeChangeCallback, displayDeviceConfig, handler, flags);
+ modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e3108c9..181386a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -139,6 +139,10 @@
boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
boolean waitForNegativeProximity);
+ void overrideDozeScreenState(int displayState);
+
+ void setDisplayOffloadSession(DisplayManagerInternal.DisplayOffloadSession session);
+
/**
* Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
* value.
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b32a207..0a1f316 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,9 @@
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -47,6 +50,7 @@
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -80,21 +84,24 @@
private final boolean mIsBootDisplayModeSupported;
+ private final DisplayManagerFlags mFlags;
+
private Context mOverlayContext;
// Called with SyncRoot lock held.
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
- Handler handler, Listener listener) {
- this(syncRoot, context, handler, listener, new Injector());
+ Handler handler, Listener listener, DisplayManagerFlags flags) {
+ this(syncRoot, context, handler, listener, flags, new Injector());
}
@VisibleForTesting
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
- Listener listener, Injector injector) {
+ Listener listener, DisplayManagerFlags flags, Injector injector) {
super(syncRoot, context, handler, listener, TAG);
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
+ mFlags = flags;
}
@Override
@@ -224,6 +231,7 @@
private boolean mAllmRequested;
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
+ private boolean mDisplayOffloadActive;
private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
// The supported display modes according to SurfaceFlinger
private SurfaceControl.DisplayMode[] mSfDisplayModes;
@@ -746,8 +754,12 @@
}
@Override
- public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
- final float sdrBrightnessState) {
+ public Runnable requestDisplayStateLocked(
+ final int state,
+ final float brightnessState,
+ final float sdrBrightnessState,
+ DisplayOffloadSession displayOffloadSession) {
+
// Assume that the brightness is off if the display is being turned off.
assert state != Display.STATE_OFF
|| brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT;
@@ -813,18 +825,39 @@
+ ", state=" + Display.stateToString(state) + ")");
}
- // We must tell sidekick to stop controlling the display before we
- // can change its power mode, so do that first.
- if (mSidekickActive) {
- Trace.traceBegin(Trace.TRACE_TAG_POWER,
- "SidekickInternal#endDisplayControl");
- try {
- mSidekickInternal.endDisplayControl();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ DisplayOffloader displayOffloader =
+ displayOffloadSession == null
+ ? null
+ : displayOffloadSession.getDisplayOffloader();
+
+ boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+
+ // We must tell sidekick/displayoffload to stop controlling the display
+ // before we can change its power mode, so do that first.
+ if (isDisplayOffloadEnabled) {
+ if (mDisplayOffloadActive && displayOffloader != null) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "DisplayOffloader#stopOffload");
+ try {
+ displayOffloader.stopOffload();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ mDisplayOffloadActive = false;
}
- mSidekickActive = false;
+ } else {
+ if (mSidekickActive) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "SidekickInternal#endDisplayControl");
+ try {
+ mSidekickInternal.endDisplayControl();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ mSidekickActive = false;
+ }
}
+
final int mode = getPowerModeForState(state);
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
+ "id=" + physicalDisplayId
@@ -836,16 +869,32 @@
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
setCommittedState(state);
+
// If we're entering a suspended (but not OFF) power state and we
- // have a sidekick available, tell it now that it can take control.
- if (Display.isSuspendedState(state) && state != Display.STATE_OFF
- && mSidekickInternal != null && !mSidekickActive) {
- Trace.traceBegin(Trace.TRACE_TAG_POWER,
- "SidekickInternal#startDisplayControl");
- try {
- mSidekickActive = mSidekickInternal.startDisplayControl(state);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ // have a sidekick/displayoffload available, tell it now that it can take
+ // control.
+ if (isDisplayOffloadEnabled) {
+ if (DisplayOffloadSession.isSupportedOffloadState(state) &&
+ displayOffloader != null
+ && !mDisplayOffloadActive) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
+ try {
+ mDisplayOffloadActive = displayOffloader.startOffload();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
+ } else {
+ if (Display.isSuspendedState(state) && state != Display.STATE_OFF
+ && mSidekickInternal != null && !mSidekickActive) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "SidekickInternal#startDisplayControl");
+ try {
+ mSidekickActive = mSidekickInternal.startDisplayControl(state);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
}
}
@@ -858,6 +907,7 @@
}
}
+
private void setDisplayBrightness(float brightnessState,
float sdrBrightnessState) {
// brightnessState includes invalid, off and full range.
@@ -1345,6 +1395,7 @@
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+ void onHotplugConnectionError(long timestampNanos, int connectionError);
void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
@@ -1367,6 +1418,11 @@
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int errorCode) {
+ mListener.onHotplugConnectionError(timestampNanos, errorCode);
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod);
@@ -1392,6 +1448,15 @@
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ if (DEBUG) {
+ Slog.d(TAG, "onHotplugConnectionError("
+ + "timestampNanos=" + timestampNanos
+ + ", connectionError=" + connectionError + ")");
+ }
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d4d104e..bd82b81 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -137,6 +137,9 @@
private final Rect mTempLayerStackRect = new Rect();
private final Rect mTempDisplayRect = new Rect();
+ /** A session token that controls the offloading operations of this logical display. */
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
/**
* Name of a display group to which the display is assigned.
*/
@@ -941,6 +944,15 @@
return mDisplayGroupName;
}
+ public void setDisplayOffloadSessionLocked(
+ DisplayManagerInternal.DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() {
+ return mDisplayOffloadSession;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mIsEnabled=" + mIsEnabled);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 5ba042c..e38c2c5 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,6 +20,8 @@
import android.util.FloatProperty;
import android.view.Choreographer;
+import com.android.internal.display.BrightnessUtils;
+
/**
* A custom animator that progressively updates a property value at
* a given variable rate until it reaches a particular target value.
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index a514136..e46edd9 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -17,9 +17,13 @@
package com.android.server.display.brightness.clamper;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.os.Handler;
+import android.os.IBinder;
import android.os.PowerManager;
+import android.view.SurfaceControlHdrLayerInfoListener;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.config.HdrBrightnessData;
import java.io.PrintWriter;
@@ -33,11 +37,18 @@
private final Runnable mDebouncer;
+ private final HdrLayerInfoListener mHdrListener;
+
@Nullable
private HdrBrightnessData mHdrBrightnessData = null;
+ @Nullable
+ private IBinder mRegisteredDisplayToken = null;
+
private float mAmbientLux = Float.MAX_VALUE;
+ private boolean mHdrVisible = false;
+
private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -47,6 +58,12 @@
public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
Handler handler) {
+ this(clamperChangeListener, handler, new Injector());
+ }
+
+ @VisibleForTesting
+ public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+ Handler handler, Injector injector) {
mClamperChangeListener = clamperChangeListener;
mHandler = handler;
mDebouncer = () -> {
@@ -54,6 +71,10 @@
mMaxBrightness = mDesiredMaxBrightness;
mClamperChangeListener.onChanged();
};
+ mHdrListener = injector.getHdrListener((visible) -> {
+ mHdrVisible = visible;
+ recalculateBrightnessCap(mHdrBrightnessData, mAmbientLux, mHdrVisible);
+ }, handler);
}
// Called in same looper: mHandler.getLooper()
@@ -72,16 +93,37 @@
*/
public void onAmbientLuxChange(float ambientLux) {
mAmbientLux = ambientLux;
- recalculateBrightnessCap(mHdrBrightnessData, ambientLux);
+ recalculateBrightnessCap(mHdrBrightnessData, ambientLux, mHdrVisible);
}
/**
* Updates brightness cap config.
* Called in same looper: mHandler.getLooper()
*/
- public void resetHdrConfig(HdrBrightnessData data) {
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public void resetHdrConfig(HdrBrightnessData data, int width, int height,
+ float minimumHdrPercentOfScreen, IBinder displayToken) {
mHdrBrightnessData = data;
- recalculateBrightnessCap(data, mAmbientLux);
+ mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+ if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
+ if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
+ mHdrListener.unregister(mRegisteredDisplayToken);
+ mHdrVisible = false;
+ }
+ if (displayToken != null) { // new token not null, subscribe
+ mHdrListener.register(displayToken);
+ }
+ mRegisteredDisplayToken = displayToken;
+ }
+ recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
+ }
+
+ /** Clean up all resources */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public void stop() {
+ if (mRegisteredDisplayToken != null) {
+ mHdrListener.unregister(mRegisteredDisplayToken);
+ }
}
/**
@@ -98,13 +140,28 @@
pw.println(" mAmbientLux=" + mAmbientLux);
}
- private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux) {
- if (data == null) {
- mHandler.removeCallbacks(mDebouncer);
+ private void reset() {
+ if (mMaxBrightness == PowerManager.BRIGHTNESS_MAX
+ && mDesiredMaxBrightness == PowerManager.BRIGHTNESS_MAX && mTransitionRate == -1f
+ && mDesiredTransitionRate == -1f) { // already done reset, do nothing
return;
}
- float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
+ mHandler.removeCallbacks(mDebouncer);
+ mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ mDesiredTransitionRate = -1f;
+ mTransitionRate = 1f;
+ mClamperChangeListener.onChanged();
+ }
+ private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux,
+ boolean hdrVisible) {
+ if (data == null || !hdrVisible) {
+ reset();
+ return;
+ }
+
+ float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
if (mMaxBrightness == expectedMaxBrightness) {
mDesiredMaxBrightness = mMaxBrightness;
mDesiredTransitionRate = -1f;
@@ -127,6 +184,8 @@
mHandler.removeCallbacks(mDebouncer);
mHandler.postDelayed(mDebouncer, debounceTime);
}
+ // do nothing if expectedMaxBrightness == mDesiredMaxBrightness
+ // && expectedMaxBrightness != mMaxBrightness
}
private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
@@ -143,4 +202,36 @@
}
return foundMaxBrightness;
}
+
+ @FunctionalInterface
+ interface HdrListener {
+ void onHdrVisible(boolean visible);
+ }
+
+ static class HdrLayerInfoListener extends SurfaceControlHdrLayerInfoListener {
+ private final HdrListener mHdrListener;
+
+ private final Handler mHandler;
+
+ private float mHdrMinPixels = Float.MAX_VALUE;
+
+ HdrLayerInfoListener(HdrListener hdrListener, Handler handler) {
+ mHdrListener = hdrListener;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, int maxW,
+ int maxH, int flags, float maxDesiredHdrSdrRatio) {
+ mHandler.post(() ->
+ mHdrListener.onHdrVisible(
+ numberOfHdrLayers > 0 && (float) (maxW * maxH) >= mHdrMinPixels));
+ }
+ }
+
+ static class Injector {
+ HdrLayerInfoListener getHdrListener(HdrListener hdrListener, Handler handler) {
+ return new HdrLayerInfoListener(hdrListener, handler);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index ff768d6..b6273e1 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,10 @@
Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
Flags::enableAdaptiveToneImprovements1);
+ private final FlagState mDisplayOffloadFlagState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
+ Flags::enableDisplayOffload);
+
private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
Flags::enableDisplayResolutionRangeVoting);
@@ -111,6 +115,11 @@
return mDisplaysRefreshRatesSynchronizationState.isEnabled();
}
+ /** Returns whether displayoffload is enabled on not */
+ public boolean isDisplayOffloadEnabled() {
+ return mDisplayOffloadFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a5b8cbb..542f26c 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -63,5 +63,12 @@
namespace: "display_manager"
description: "Enables synchronization of refresh rates across displays"
bug: "294015845"
+}
+
+flag {
+ name: "enable_display_offload"
+ namespace: "display_manager"
+ description: "Feature flag for DisplayOffload"
+ bug: "299521647"
is_fixed_read_only: true
}
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index b1a1c60..5d6e650 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -32,6 +32,7 @@
public class DisplayStateController {
private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
private boolean mPerformScreenOffTransition = false;
+ private int mDozeStateOverride = Display.STATE_UNKNOWN;
public DisplayStateController(DisplayPowerProximityStateController
displayPowerProximityStateController) {
@@ -65,6 +66,7 @@
} else {
state = Display.STATE_DOZE;
}
+ state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
break;
case DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM:
case DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT:
@@ -84,6 +86,10 @@
return state;
}
+ public void overrideDozeScreenState(int displayState) {
+ mDozeStateOverride = displayState;
+ }
+
/**
* Checks if the screen off transition is to be performed or not.
*/
@@ -100,6 +106,8 @@
pw.println();
pw.println("DisplayStateController:");
pw.println(" mPerformScreenOffTransition:" + mPerformScreenOffTransition);
+ pw.println(" mDozeStateOverride=" + mDozeStateOverride);
+
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
if (mDisplayPowerProximityStateController != null) {
mDisplayPowerProximityStateController.dumpLocal(ipw);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c3abfc1..f168f43 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -349,17 +349,17 @@
@Override
public void onUserStarting(@NonNull TargetUser user) {
- mLockSettingsService.onStartUser(user.getUserIdentifier());
+ mLockSettingsService.onUserStarting(user.getUserIdentifier());
}
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- mLockSettingsService.onUnlockUser(user.getUserIdentifier());
+ mLockSettingsService.onUserUnlocking(user.getUserIdentifier());
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
- mLockSettingsService.onCleanupUser(user.getUserIdentifier());
+ mLockSettingsService.onUserStopped(user.getUserIdentifier());
}
}
@@ -784,7 +784,7 @@
}
@VisibleForTesting
- void onCleanupUser(int userId) {
+ void onUserStopped(int userId) {
hideEncryptionNotification(new UserHandle(userId));
// User is stopped with its CE key evicted. Restore strong auth requirement to the default
// flags after boot since stopping and restarting a user later is equivalent to rebooting
@@ -796,7 +796,7 @@
}
}
- private void onStartUser(final int userId) {
+ private void onUserStarting(final int userId) {
maybeShowEncryptionNotificationForUser(userId, "user started");
}
@@ -832,7 +832,7 @@
}
}
- private void onUnlockUser(final int userId) {
+ private void onUserUnlocking(final int userId) {
// Perform tasks which require locks in LSS on a handler, as we are callbacks from
// ActivityManager.unlockUser()
mHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4892c22..83a3125 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.media.MediaFeatureFlagManager.FEATURE_SCANNING_MINIMUM_PACKAGE_IMPORTANCE;
@@ -487,12 +488,13 @@
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
+ final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerManagerLocked(manager, callerUid, callerPid, callerPackageName, userId);
+ registerManagerLocked(
+ manager, callerUid, callerPid, callerPackageName, callerUserId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1156,8 +1158,12 @@
}
@GuardedBy("mLock")
- private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
- int callerUid, int callerPid, @NonNull String callerPackageName, int userId) {
+ private void registerManagerLocked(
+ @NonNull IMediaRouter2Manager manager,
+ int callerUid,
+ int callerPid,
+ @NonNull String callerPackageName,
+ int callerUserId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1167,14 +1173,17 @@
return;
}
- Slog.i(TAG, TextUtils.formatSimple(
- "registerManager | callerUid: %d, callerPid: %d, package: %s, user: %d",
- callerUid, callerPid, callerPackageName, userId));
+ Slog.i(
+ TAG,
+ TextUtils.formatSimple(
+ "registerManager | callerUid: %d, callerPid: %d, callerPackage: %s,"
+ + " callerUserId: %d",
+ callerUid, callerPid, callerPackageName, callerUserId));
mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
"Must hold MEDIA_CONTENT_CONTROL permission.");
- UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+ UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId);
managerRecord = new ManagerRecord(
userRecord, manager, callerUid, callerPid, callerPackageName);
try {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c6f6fe2..fe91050 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1021,7 +1021,7 @@
synchronized (mSnoozing) {
mSnoozing.remove(user);
}
- rebindServices(true, user);
+ unbindUserServices(user);
}
public void onUserSwitched(int user) {
@@ -1408,12 +1408,24 @@
void unbindOtherUserServices(int currentUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
- final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+ unbindServicesImpl(currentUser, true /* allExceptUser */);
+ t.traceEnd();
+ }
+ void unbindUserServices(int user) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("ManagedServices.unbindUserServices" + user);
+ unbindServicesImpl(user, false /* allExceptUser */);
+ t.traceEnd();
+ }
+
+ void unbindServicesImpl(int user, boolean allExceptUser) {
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
- if (info.userid != currentUser) {
+ if ((allExceptUser && (info.userid != user))
+ || (!allExceptUser && (info.userid == user))) {
Set<ComponentName> toUnbind =
componentsToUnbind.get(info.userid, new ArraySet<>());
toUnbind.add(info.component);
@@ -1422,7 +1434,6 @@
}
}
unbindFromServices(componentsToUnbind);
- t.traceEnd();
}
protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 802dfb1..a3c71c2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -177,6 +177,7 @@
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
@@ -263,6 +264,7 @@
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -4159,7 +4161,7 @@
String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, Binder.getCallingUid(), false, false, true);
+ pkg, Binder.getCallingUid(), false, false, true, true, null);
}
@Override
@@ -4280,7 +4282,36 @@
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, uid, includeDeleted, true, false);
+ pkg, uid, includeDeleted, true, false, true, null);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannelGroup>
+ getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid) {
+ enforceSystemOrSystemUI("getRecentBlockedNotificationChannelGroupsForPackage");
+ Set<String> recentlySentChannels = new HashSet<>();
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * 14);
+ UsageEvents events = mUsageStatsManagerInternal.queryEventsForUser(
+ UserHandle.getUserId(uid), startTime, now, UsageEvents.SHOW_ALL_EVENT_DATA);
+ // get all channelids that sent notifs in the past 2 weeks
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (pkg.equals(event.mPackage)) {
+ String channelId = event.mNotificationChannelId;
+ if (channelId != null) {
+ recentlySentChannels.add(channelId);
+ }
+ }
+ }
+ }
+ }
+
+ return mPreferencesHelper.getNotificationChannelGroups(
+ pkg, uid, false, true, false, true, recentlySentChannels);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b132a23..de698d9 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1459,9 +1459,9 @@
}
}
- @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
+ int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty,
+ boolean includeBlocked, Set<String> activeChannelFilter) {
Objects.requireNonNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
@@ -1473,7 +1473,11 @@
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
+ boolean includeChannel = (includeDeleted || !nc.isDeleted())
+ && (activeChannelFilter == null
+ || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
+ || activeChannelFilter.contains(nc.getId()));
+ if (includeChannel) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
NotificationChannelGroup ncg = groups.get(nc.getGroup());
@@ -1481,7 +1485,6 @@
ncg = r.groups.get(nc.getGroup()).clone();
ncg.setChannels(new ArrayList<>());
groups.put(nc.getGroup(), ncg);
-
}
ncg.addChannel(nc);
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index fec3591..8df24c9 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -40,8 +40,6 @@
int uid);
void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi);
- ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess, int callingUid,
boolean isSystemOrSystemUi);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 52eef47..b9464d9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -18,9 +18,6 @@
import static android.app.AppGlobals.getPackageManager;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -31,10 +28,10 @@
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
+import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
-
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
import android.annotation.NonNull;
@@ -82,6 +79,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -261,6 +259,8 @@
private final OverlayActorEnforcer mActorEnforcer;
+ private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor();
+
private int mPrevStartedUserId = -1;
public OverlayManagerService(@NonNull final Context context) {
@@ -277,16 +277,9 @@
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
- HandlerThread packageReceiverThread = new HandlerThread(TAG);
- packageReceiverThread.start();
-
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, packageReceiverThread.getThreadHandler());
+ HandlerThread packageMonitorThread = new HandlerThread(TAG);
+ packageMonitorThread.start();
+ mPackageMonitor.register(context, packageMonitorThread.getLooper(), true);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -372,166 +365,171 @@
return defaultPackages.toArray(new String[defaultPackages.size()]);
}
- private final class PackageReceiver extends BroadcastReceiver {
+ private final class OverlayManagerPackageMonitor extends PackageMonitor {
+
@Override
- public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
- final String action = intent.getAction();
- if (action == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null action");
- return;
- }
- final Uri data = intent.getData();
- if (data == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null data");
- return;
- }
- final String packageName = data.getSchemeSpecificPart();
-
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- final boolean systemUpdateUninstall =
- intent.getBooleanExtra(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
-
- final int[] userIds;
- final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
- if (extraUid == UserHandle.USER_NULL) {
- userIds = mUserManager.getUserIds();
- } else {
- userIds = new int[] { UserHandle.getUserId(extraUid) };
- }
-
- switch (action) {
- case ACTION_PACKAGE_ADDED:
- if (replacing) {
- onPackageReplaced(packageName, userIds);
- } else {
- onPackageAdded(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_CHANGED:
- // ignore the intent if it was sent by the package manager as a result of the
- // overlay manager having sent ACTION_OVERLAY_CHANGED
- if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
- onPackageChanged(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_REMOVED:
- if (replacing) {
- onPackageReplacing(packageName, systemUpdateUninstall, userIds);
- } else {
- onPackageRemoved(packageName, userIds);
- }
- break;
- default:
- // do nothing
- break;
- }
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ handlePackageAdd(packageName, extras);
}
- private void onPackageAdded(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
- for (final int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageAdded(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageAdded(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageAdded internal error", e);
- }
+ @Override
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ handlePackageChange(packageName, extras);
+ }
+
+ @Override
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ handlePackageRemove(packageName, extras);
+ }
+ }
+
+ private int[] getUserIds(int uid) {
+ final int[] userIds;
+ if (uid == INVALID_UID) {
+ userIds = mUserManager.getUserIds();
+ } else {
+ userIds = new int[] { UserHandle.getUserId(uid) };
+ }
+ return userIds;
+ }
+
+ private void handlePackageAdd(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (replacing) {
+ onPackageReplaced(packageName, userIds);
+ } else {
+ onPackageAdded(packageName, userIds);
+ }
+ }
+
+ private void handlePackageChange(String packageName, Bundle extras) {
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) {
+ onPackageChanged(packageName, userIds);
+ }
+ }
+
+ private void handlePackageRemove(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final boolean systemUpdateUninstall =
+ extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+
+ if (replacing) {
+ onPackageReplacing(packageName, systemUpdateUninstall, userIds);
+ } else {
+ onPackageRemoved(packageName, userIds);
+ }
+ }
+
+ private void onPackageAdded(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
+ for (final int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageAdded(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageAdded(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageChanged(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageChanged(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageChanged internal error", e);
- }
+ private void onPackageChanged(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageChanged(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplacing(@NonNull final String packageName,
- boolean systemUpdateUninstall, @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
- systemUpdateUninstall, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplacing internal error", e);
- }
+ private void onPackageReplacing(@NonNull final String packageName,
+ boolean systemUpdateUninstall, @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
+ systemUpdateUninstall, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplaced(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageReplaced(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplaced internal error", e);
- }
+ private void onPackageReplaced(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageReplaced(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageRemoved(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- mPackageManager.onPackageRemoved(packageName, userId);
- updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
- }
+ private void onPackageRemoved(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ mPackageManager.onPackageRemoved(packageName, userId);
+ updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
}
@@ -684,7 +682,7 @@
synchronized (mLock) {
try {
mImpl.setEnabledExclusive(
- overlay, false /* withinCategory */, realUserId)
+ overlay, false /* withinCategory */, realUserId)
.ifPresent(
OverlayManagerService.this::updateTargetPackagesLocked);
return true;
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 9a69d77..e367609 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -17,9 +17,14 @@
package com.android.server.pm;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.SYSTEM_UID;
import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -28,6 +33,7 @@
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.BroadcastOptions;
@@ -38,12 +44,18 @@
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerExemptionManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
+import android.stats.storage.StorageEnums;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -51,10 +63,15 @@
import android.util.SparseArray;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.function.BiFunction;
-import java.util.function.Supplier;
/**
* Helper class to send broadcasts for various situations.
@@ -70,14 +87,20 @@
private final UserManagerInternal mUmInternal;
private final ActivityManagerInternal mAmInternal;
private final Context mContext;
+ private final Handler mHandler;
+ private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+ private final AppsFilterSnapshot mAppsFilter;
BroadcastHelper(PackageManagerServiceInjector injector) {
mUmInternal = injector.getUserManagerInternal();
mAmInternal = injector.getActivityManagerInternal();
mContext = injector.getContext();
+ mHandler = injector.getHandler();
+ mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
+ mAppsFilter = injector.getAppsFilter();
}
- public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
@@ -114,9 +137,16 @@
* the system and applications allowed to see instant applications to receive package
* lifecycle events for instant applications.
*/
- public void doSendBroadcast(String action, String pkg, Bundle extras,
- int flags, String targetPkg, IIntentReceiver finishedReceiver,
- int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
+ private void doSendBroadcast(
+ @NonNull String action,
+ @Nullable String pkg,
+ @Nullable Bundle extras,
+ int flags,
+ @Nullable String targetPkg,
+ @Nullable IIntentReceiver finishedReceiver,
+ @NonNull int[] userIds,
+ boolean isInstantApp,
+ @Nullable SparseArray<int[]> broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
@Nullable Bundle bOptions) {
for (int userId : userIds) {
@@ -166,9 +196,11 @@
}
}
- public void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotComputer,
- boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames,
- @NonNull int[] uids) {
+ void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
+ boolean mediaStatus,
+ boolean replacing,
+ @NonNull String[] pkgNames,
+ @NonNull int[] uids) {
if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) {
return;
}
@@ -184,7 +216,7 @@
null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshotComputer.get(), callingUid, intentExtras),
+ snapshot, callingUid, intentExtras),
null /* bOptions */);
}
@@ -193,8 +225,9 @@
* automatically without needing an explicit launch.
* Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
*/
- public void sendBootCompletedBroadcastToSystemApp(
- String packageName, boolean includeStopped, int userId) {
+ private void sendBootCompletedBroadcastToSystemApp(@NonNull String packageName,
+ boolean includeStopped,
+ int userId) {
// If user is not running, the app didn't miss any broadcast
if (!mUmInternal.isUserRunning(userId)) {
return;
@@ -229,7 +262,7 @@
}
}
- public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
+ private @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@PowerExemptionManager.ReasonCode int reasonCode) {
long duration = 10_000;
if (mAmInternal != null) {
@@ -242,9 +275,14 @@
return bOptions;
}
- public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
- ArrayList<String> componentNames, int packageUid, String reason,
- int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ private void sendPackageChangedBroadcast(@NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @Nullable String reason,
+ @Nullable int[] userIds,
+ @Nullable int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList) {
if (DEBUG_INSTALL) {
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
@@ -269,7 +307,7 @@
null /* bOptions */);
}
- public static void sendDeviceCustomizationReadyBroadcast() {
+ static void sendDeviceCustomizationReadyBroadcast() {
final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
final IActivityManager am = ActivityManager.getService();
@@ -285,15 +323,23 @@
}
}
- public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
- int launcherUid, @Nullable ComponentName launcherComponent,
- @Nullable String appPredictionServicePackage) {
+ void sendSessionCommitBroadcast(@NonNull Computer snapshot,
+ @NonNull PackageInstaller.SessionInfo sessionInfo,
+ int userId,
+ @Nullable String appPredictionServicePackage) {
+ UserManagerService ums = UserManagerService.getInstance();
+ if (ums == null || sessionInfo.isStaged()) {
+ return;
+ }
+ final UserInfo parent = ums.getProfileParent(userId);
+ final int launcherUserId = (parent != null) ? parent.id : userId;
+ final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId);
if (launcherComponent != null) {
Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(launcherComponent.getPackageName());
- mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+ mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId));
}
// TODO(b/122900055) Change/Remove this and replace with new permission role.
if (appPredictionServicePackage != null) {
@@ -301,30 +347,278 @@
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(appPredictionServicePackage);
- mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
+ mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUserId));
}
}
- public void sendPreferredActivityChangedBroadcast(int userId) {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
+ void sendPreferredActivityChangedBroadcast(int userId) {
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ return;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ try {
+ am.broadcastIntentWithFeature(null, null, intent, null, null,
+ 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
+ null, false, false, userId);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+
+ void sendPostInstallBroadcasts(@NonNull Computer snapshot,
+ @NonNull InstallRequest request,
+ @NonNull String packageName,
+ @NonNull String requiredPermissionControllerPackage,
+ @NonNull String[] requiredVerifierPackages,
+ @NonNull String requiredInstallerPackage,
+ @NonNull PackageSender packageSender,
+ boolean isLaunchedForRestore,
+ boolean isKillApp,
+ boolean isUpdate,
+ boolean isArchived) {
+ // Send the removed broadcasts
+ if (request.getRemovedInfo() != null) {
+ if (request.getRemovedInfo().mIsExternal) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final String[] pkgNames = new String[]{
+ request.getRemovedInfo().mRemovedPackage};
+ final int[] uids = new int[]{request.getRemovedInfo().mUid};
+ notifyResourcesChanged(
+ false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ sendResourcesChangedBroadcast(
+ snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ }
+ sendPackageRemovedBroadcasts(
+ request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/,
+ false /*isArchived*/);
+ }
+
+ final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
+ final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
+ final int[] updateUserIds = request.getUpdateBroadcastUserIds();
+ final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
+
+ final String installerPackageName =
+ request.getInstallerPackageName() != null
+ ? request.getInstallerPackageName()
+ : request.getRemovedInfo() != null
+ ? request.getRemovedInfo().mInstallerPackageName
+ : null;
+
+ Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, request.getAppId());
+ if (isUpdate) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ if (isArchived) {
+ extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+ }
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, request.getDataLoaderType());
+
+ final String staticSharedLibraryName = request.getPkg().getStaticSharedLibraryName();
+ // If a package is a static shared library, then only the installer of the package
+ // should get the broadcast.
+ if (installerPackageName != null && staticSharedLibraryName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ request.getNewUsers(), null /* instantUserIds*/,
+ null /* broadcastAllowList */, null);
+ }
+
+ // Send installed broadcasts if the package is not a static shared lib.
+ if (staticSharedLibraryName == null) {
+ // Send PACKAGE_ADDED broadcast for users that see the package for the first time
+ // sendPackageAddedForNewUsers also deals with system apps
+ final int appId = UserHandle.getAppId(request.getAppId());
+ final boolean isSystem = request.isInstallSystem();
+ final boolean isVirtualPreload =
+ ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ sendPackageAddedForNewUsers(snapshot, packageName,
+ isSystem || isVirtualPreload,
+ isVirtualPreload /*startReceiver*/, appId,
+ firstUserIds, firstInstantUserIds, isArchived, request.getDataLoaderType());
+
+ // Send PACKAGE_ADDED broadcast for users that don't see
+ // the package for the first time
+
+ // Send to all running apps.
+ final SparseArray<int[]> newBroadcastAllowList =
+ mAppsFilter.getVisibilityAllowList(snapshot,
+ snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+ updateUserIds, snapshot.getPackageStates());
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, newBroadcastAllowList, null);
+ // Send to the installer, even if it's not running.
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // Send to PermissionController for all update users, even if it may not be running
+ // for some users
+ if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ requiredPermissionControllerPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // Notify required verifier(s) that are not the installer of record for the package.
+ for (String verifierPackageName : requiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+ packageName,
+ extras, 0 /*flags*/,
+ verifierPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */,
+ null);
+ }
+ }
+ // If package installer is defined, notify package installer about new
+ // app installed
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+ requiredInstallerPackage, null /*finishedReceiver*/,
+ firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+
+ // Send replaced for users that don't see the package for the first time
+ if (isUpdate) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds,
+ request.getRemovedInfo().mBroadcastAllowList, null);
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ for (String verifierPackageName : requiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/, verifierPackageName,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/, null);
+ }
+ }
+ sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/,
+ getTemporaryAppAllowlistBroadcastOptions(
+ REASON_PACKAGE_REPLACED).toBundle());
+ } else if (isLaunchedForRestore && !request.isInstallSystem()) {
+ // First-install and we did a restore, so we're responsible for the
+ // first-launch broadcast.
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Post-restore of " + packageName
+ + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+ }
+ sendFirstLaunchBroadcast(packageName, installerPackageName,
+ firstUserIds, firstInstantUserIds);
+ }
+
+ // Send broadcast package appeared if external for all users
+ if (request.getPkg().isExternalStorage()) {
+ if (!isUpdate) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ VolumeInfo volume =
+ storage.findVolumeByUuid(
+ StorageManager.convert(
+ request.getPkg().getVolumeUuid()).toString());
+ int packageExternalStorageType =
+ PackageManagerServiceUtils.getPackageExternalStorageType(volume,
+ /* isExternalStorage */ true);
+ // If the package was installed externally, log it.
+ if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ packageExternalStorageType, packageName);
+ }
+ }
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + packageName + " is external");
+ }
+ if (!isArchived) {
+ final String[] pkgNames = new String[]{packageName};
+ final int[] uids = new int[]{request.getPkg().getUid()};
+ sendResourcesChangedBroadcast(snapshot,
+ true /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ notifyResourcesChanged(true /* mediaStatus */,
+ true /* replacing */, pkgNames, uids);
+ }
+ }
+ } else { // if static shared lib
+ final ArrayList<AndroidPackage> libraryConsumers = request.getLibraryConsumers();
+ if (!ArrayUtils.isEmpty(libraryConsumers)) {
+ // No need to kill consumers if it's installation of new version static shared lib.
+ final boolean dontKillApp = !isUpdate;
+ for (int i = 0; i < libraryConsumers.size(); i++) {
+ AndroidPackage pkg = libraryConsumers.get(i);
+ // send broadcast that all consumers of the static shared library have changed
+ sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+ dontKillApp,
+ new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+ pkg.getUid(), null);
+ }
+ }
+ }
+ }
+
+ private void sendPackageAddedForNewUsers(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ boolean sendBootCompleted,
+ boolean includeStopped,
+ @AppIdInt int appId,
+ int[] userIds,
+ int[] instantUserIds,
+ boolean isArchived,
+ int dataLoaderType) {
+ if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
return;
}
-
- final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- try {
- am.broadcastIntentWithFeature(null, null, intent, null, null,
- 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
- null, false, false, userId);
- } catch (RemoteException e) {
+ SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
+ snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+ userIds, snapshot.getPackageStates());
+ mHandler.post(
+ () -> sendPackageAddedForNewUsers(packageName, appId, userIds,
+ instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
+ instantUserIds, isArchived, dataLoaderType, broadcastAllowList, mHandler);
+ if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
+ mHandler.post(() -> {
+ for (int userId : userIds) {
+ sendBootCompletedBroadcastToSystemApp(
+ packageName, includeStopped, userId);
+ }
+ }
+ );
}
}
- public void sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType,
- SparseArray<int[]> broadcastAllowlist) {
+ private void sendPackageAddedForNewUsers(@NonNull String packageName,
+ @AppIdInt int appId,
+ int[] userIds,
+ int[] instantUserIds,
+ boolean isArchived,
+ int dataLoaderType,
+ @NonNull SparseArray<int[]> broadcastAllowlist) {
Bundle extras = new Bundle(1);
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
final int uid = UserHandle.getUid(
@@ -349,7 +643,30 @@
}
}
- public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+ void sendPackageAddedForUser(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ @NonNull PackageStateInternal packageState,
+ int userId,
+ boolean isArchived,
+ int dataLoaderType,
+ @Nullable String appPredictionServicePackage) {
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ final boolean isSystem = packageState.isSystem();
+ final boolean isInstantApp = userState.isInstantApp();
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
+ false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
+ isArchived, dataLoaderType);
+
+ // Send a session commit broadcast
+ final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
+ info.installReason = userState.getInstallReason();
+ info.appPackageName = packageName;
+ sendSessionCommitBroadcast(snapshot, info, userId, appPredictionServicePackage);
+ }
+
+ void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
int[] userIds, int[] instantUserIds) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
@@ -366,7 +683,7 @@
* access all the packages in the extras.
*/
@Nullable
- public static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
+ private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
@NonNull Bundle extras) {
if (UserHandle.isCore(callingUid)) {
// see all
@@ -392,7 +709,7 @@
}
/** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
- public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
+ private static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
PackageManager packageManager = context.getPackageManager();
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true)
@@ -424,4 +741,323 @@
pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null,
uidList != null && uidList.size() > 0 ? uidList.toArray() : null);
}
+
+ void sendApplicationHiddenForUser(@NonNull String packageName,
+ @NonNull PackageStateInternal packageState,
+ int userId,
+ @NonNull PackageSender packageSender) {
+ final PackageRemovedInfo info = new PackageRemovedInfo();
+ info.mRemovedPackage = packageName;
+ info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
+ info.mRemovedUsers = new int[] {userId};
+ info.mBroadcastUsers = new int[] {userId};
+ info.mUid = UserHandle.getUid(userId, packageState.getAppId());
+ info.mRemovedPackageVersionCode = packageState.getVersionCode();
+ sendPackageRemovedBroadcasts(info, packageSender, true /*killApp*/,
+ false /*removedBySystem*/, false /*isArchived*/);
+ }
+
+ void sendPackageChangedBroadcast(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @NonNull String reason) {
+ PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
+ Process.SYSTEM_UID);
+ if (setting == null) {
+ return;
+ }
+ final int userId = UserHandle.getUserId(packageUid);
+ final boolean isInstantApp =
+ snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ final SparseArray<int[]> broadcastAllowList =
+ isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
+ mHandler.post(() -> sendPackageChangedBroadcast(
+ packageName, dontKillApp, componentNames, packageUid, reason, userIds,
+ instantUserIds, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
+ packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
+ }
+
+ private void sendPackageBroadcastAndNotify(@NonNull String action,
+ @NonNull String pkg,
+ @NonNull Bundle extras,
+ int flags,
+ @Nullable String targetPkg,
+ @Nullable IIntentReceiver finishedReceiver,
+ @NonNull int[] userIds,
+ @NonNull int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable Bundle bOptions) {
+ mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags,
+ targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
+ null /* filterExtrasForReceiver */, bOptions));
+ if (targetPkg == null) {
+ // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
+ // many times to different targets, e.g. installer app, permission controller, other
+ // registered apps. We should filter it to avoid calling back many times for the same
+ // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
+ // installer app or null for registered apps. The callback only need to send back to the
+ // registered apps so we check the null condition here.
+ notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
+ }
+ }
+
+ void sendSystemPackageUpdatedBroadcasts(@NonNull PackageRemovedInfo packageRemovedInfo) {
+ if (!packageRemovedInfo.mIsRemovedPackageSystemUpdate) {
+ return;
+ }
+
+ final String removedPackage = packageRemovedInfo.mRemovedPackage;
+ final int removedAppId = packageRemovedInfo.mRemovedAppId;
+ final int uid = packageRemovedInfo.mUid;
+ final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+ final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+
+ Bundle extras = new Bundle(2);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
+ 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ removedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryBroadcastOptionsForSystemPackageUpdate(REASON_PACKAGE_REPLACED)
+ .toBundle());
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private @NonNull BroadcastOptions getTemporaryBroadcastOptionsForSystemPackageUpdate(
+ @PowerExemptionManager.ReasonCode int reasonCode) {
+ long duration = 10_000;
+ if (mAmInternal != null) {
+ duration = mAmInternal.getBootTimeTempAllowListDuration();
+ }
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ reasonCode, "");
+ return bOptions;
+ }
+
+
+ void sendPackageRemovedBroadcasts(
+ @NonNull PackageRemovedInfo packageRemovedInfo,
+ @NonNull PackageSender packageSender,
+ boolean killApp,
+ boolean removedBySystem,
+ boolean isArchived) {
+ final String removedPackage = packageRemovedInfo.mRemovedPackage;
+ final int removedAppId = packageRemovedInfo.mRemovedAppId;
+ final int uid = packageRemovedInfo.mUid;
+ final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+ final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers;
+ final int[] instantUserIds = packageRemovedInfo.mInstantUserIds;
+ final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+ final boolean dataRemoved = packageRemovedInfo.mDataRemoved;
+ final boolean isUpdate = packageRemovedInfo.mIsUpdate;
+ final boolean isRemovedPackageSystemUpdate =
+ packageRemovedInfo.mIsRemovedPackageSystemUpdate;
+ final boolean isRemovedForAllUsers = packageRemovedInfo.mRemovedForAllUsers;
+ final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib;
+
+ Bundle extras = new Bundle();
+ final int removedUid = removedAppId >= 0 ? removedAppId : uid;
+ extras.putInt(Intent.EXTRA_UID, removedUid);
+ extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
+ extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
+ extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
+ final boolean isReplace = isUpdate || isRemovedPackageSystemUpdate;
+ if (isReplace || isArchived) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ if (isArchived) {
+ extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+ }
+ extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, isRemovedForAllUsers);
+
+ // Send PACKAGE_REMOVED broadcast to the respective installer.
+ if (removedPackage != null && installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, broadcastUserIds, instantUserIds, null, null);
+ }
+ if (isStaticSharedLib) {
+ // When uninstalling static shared libraries, only the package's installer needs to be
+ // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
+ return;
+ }
+ if (removedPackage != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0, null /*targetPackage*/, null,
+ broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+ removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+ null /*finishedReceiver*/, broadcastUserIds, instantUserIds,
+ broadcastAllowList, null /*bOptions*/);
+ if (dataRemoved && !isRemovedPackageSystemUpdate) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
+ null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ packageSender.notifyPackageRemoved(removedPackage, removedUid);
+ }
+ }
+ if (removedAppId >= 0) {
+ // If a system app's updates are uninstalled the UID is not actually removed. Some
+ // services need to know the package name affected.
+ if (isReplace) {
+ extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
+ }
+
+ sendPackageBroadcastAndNotify(Intent.ACTION_UID_REMOVED,
+ null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ }
+ }
+
+ /**
+ * Send broadcast intents for packages suspension changes.
+ *
+ * @param intent The action name of the suspension intent.
+ * @param pkgList The names of packages which have suspension changes.
+ * @param uidList The uids of packages which have suspension changes.
+ * @param userId The user where packages reside.
+ */
+ void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot,
+ @NonNull String intent,
+ @NonNull String[] pkgList,
+ @NonNull int[] uidList,
+ boolean quarantined,
+ int userId) {
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ if (quarantined) {
+ extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
+ }
+ final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
+ final Bundle options = new BroadcastOptions()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+ .toBundle();
+ mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
+ extras, flags, null /* targetPkg */, null /* finishedReceiver */,
+ new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
+ (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
+ snapshot, callingUid, intentExtras),
+ options));
+ notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
+ null /* instantUserIds */, null /* broadcastAllowList */);
+ }
+
+ void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
+ @NonNull String[] affectedPackages,
+ boolean suspended,
+ int userId) {
+ final String action = suspended
+ ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+ : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+ return;
+ }
+ final int[] targetUserIds = new int[] {userId};
+ for (String packageName : affectedPackages) {
+ final Bundle appExtras = suspended
+ ? SuspendPackageHelper.getSuspendedPackageAppExtras(
+ snapshot, packageName, userId, SYSTEM_UID)
+ : null;
+ final Bundle intentExtras;
+ if (appExtras != null) {
+ intentExtras = new Bundle(1);
+ intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+ } else {
+ intentExtras = null;
+ }
+ doSendBroadcast(action, null, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+ targetUserIds, false, null, null, null);
+ }
+ });
+ }
+
+ /**
+ * Send broadcast intents for packages distracting changes.
+ *
+ * @param pkgList The names of packages which have suspension changes.
+ * @param uidList The uids of packages which have suspension changes.
+ * @param userId The user where packages reside.
+ */
+ void sendDistractingPackagesChanged(@NonNull Computer snapshot,
+ @NonNull String[] pkgList,
+ @NonNull int[] uidList,
+ int userId,
+ int distractionFlags) {
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+
+ mHandler.post(() -> sendPackageBroadcast(
+ Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
+ extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+ null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
+ null /* broadcastAllowList */,
+ (callingUid, intentExtras) -> filterExtrasChangedPackageList(
+ snapshot, callingUid, intentExtras),
+ null /* bOptions */));
+ }
+
+ void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
+ boolean mediaStatus,
+ boolean replacing,
+ @NonNull ArrayList<AndroidPackage> packages) {
+ final int size = packages.size();
+ final String[] packageNames = new String[size];
+ final int[] packageUids = new int[size];
+ for (int i = 0; i < size; i++) {
+ final AndroidPackage pkg = packages.get(i);
+ packageNames[i] = pkg.getPackageName();
+ packageUids[i] = pkg.getUid();
+ }
+ sendResourcesChangedBroadcast(snapshot, mediaStatus,
+ replacing, packageNames, packageUids);
+ notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
+ }
+
+ private void notifyPackageMonitor(@NonNull String action,
+ @NonNull String pkg,
+ @Nullable Bundle extras,
+ @NonNull int[] userIds,
+ @NonNull int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList) {
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
+ instantUserIds, broadcastAllowList, mHandler);
+ }
+
+ private void notifyResourcesChanged(boolean mediaStatus,
+ boolean replacing,
+ @NonNull String[] pkgNames,
+ @NonNull int[] uids) {
+ mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
+ uids, mHandler);
+ }
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 83f90a1..8e767e7 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -63,7 +63,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -87,19 +86,16 @@
private final PackageManagerService mPm;
private final UserManagerInternal mUserManagerInternal;
- private final PermissionManagerServiceInternal mPermissionManager;
private final RemovePackageHelper mRemovePackageHelper;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) {
+ DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
- mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = removePackageHelper;
- }
-
- DeletePackageHelper(PackageManagerService pm) {
- this(pm, new RemovePackageHelper(pm));
+ mBroadcastHelper = broadcastHelper;
}
/**
@@ -121,7 +117,7 @@
*/
public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
boolean removedBySystem) {
- final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
@@ -251,8 +247,9 @@
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
- info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
- info.sendSystemPackageUpdatedBroadcasts();
+ mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, killApp,
+ removedBySystem, isArchived);
+ mBroadcastHelper.sendSystemPackageUpdatedBroadcasts(info);
PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
}
@@ -314,7 +311,7 @@
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs);
+ mPm.enableCompressedPackage(stubPkg, stubPs);
} else if (DEBUG_COMPRESSION) {
Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ "after removal; pkg: " + stubPkg.getPackageName());
@@ -491,8 +488,7 @@
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
deleteInstalledSystemPackage(action, allUserHandles, writeSettings);
- new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF(
- action, allUserHandles, writeSettings);
+ mPm.restoreDisabledSystemPackageLIF(action, allUserHandles, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
if (ps.isIncremental()) {
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index 8ebb6ea..c5ec73b 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -19,10 +19,7 @@
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import android.annotation.NonNull;
-import android.content.Intent;
import android.content.pm.PackageManager.DistractionRestriction;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
@@ -42,17 +39,16 @@
// TODO(b/198166813): remove PMS dependency
private final PackageManagerService mPm;
- private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
private final SuspendPackageHelper mSuspendPackageHelper;
/**
* Constructor for {@link PackageManagerService}.
*/
- DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
- BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) {
+ DistractingPackageHelper(PackageManagerService pm,
+ BroadcastHelper broadcastHelper,
+ SuspendPackageHelper suspendPackageHelper) {
mPm = pm;
- mInjector = injector;
mBroadcastHelper = broadcastHelper;
mSuspendPackageHelper = suspendPackageHelper;
}
@@ -127,8 +123,8 @@
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
- restrictionFlags);
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ changedPackages, changedUids.toArray(), userId, restrictionFlags);
mPm.scheduleWritePackageRestrictions(userId);
}
return unactionedPackages.toArray(new String[0]);
@@ -202,34 +198,9 @@
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
- sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId,
- RESTRICTION_NONE);
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ packageArray, changedUids.toArray(), userId, RESTRICTION_NONE);
mPm.scheduleWritePackageRestrictions(userId);
}
}
-
- /**
- * Send broadcast intents for packages distracting changes.
- *
- * @param pkgList The names of packages which have suspension changes.
- * @param uidList The uids of packages which have suspension changes.
- * @param userId The user where packages reside.
- */
- void sendDistractingPackagesChanged(@NonNull String[] pkgList, int[] uidList, int userId,
- int distractionFlags) {
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
-
- final Handler handler = mInjector.getHandler();
- handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
- extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
- null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
- null /* broadcastAllowList */,
- (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- mPm.snapshotComputer(), callingUid, intentExtras),
- null /* bOptions */));
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 486b1ad..8f71a9b 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -38,7 +38,6 @@
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -50,7 +49,6 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
-import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -130,7 +128,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Message;
@@ -143,9 +140,6 @@
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -163,7 +157,6 @@
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
@@ -220,6 +213,7 @@
private final AppDataHelper mAppDataHelper;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
+ private final DeletePackageHelper mDeletePackageHelper;
private final IncrementalManager mIncrementalManager;
private final ApexManager mApexManager;
private final DexManager mDexManager;
@@ -233,12 +227,17 @@
private final UpdateOwnershipHelper mUpdateOwnershipHelper;
// TODO(b/198166813): remove PMS dependency
- InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+ InstallPackageHelper(PackageManagerService pm,
+ AppDataHelper appDataHelper,
+ RemovePackageHelper removePackageHelper,
+ DeletePackageHelper deletePackageHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
- mBroadcastHelper = new BroadcastHelper(pm.mInjector);
- mRemovePackageHelper = new RemovePackageHelper(pm);
+ mBroadcastHelper = broadcastHelper;
+ mRemovePackageHelper = removePackageHelper;
+ mDeletePackageHelper = deletePackageHelper;
mIncrementalManager = pm.mInjector.getIncrementalManager();
mApexManager = pm.mInjector.getApexManager();
mDexManager = pm.mInjector.getDexManager();
@@ -251,10 +250,6 @@
mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
}
- InstallPackageHelper(PackageManagerService pm) {
- this(pm, new AppDataHelper(pm));
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -263,7 +258,7 @@
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy("mPm.mLock")
- public AndroidPackage commitReconciledScanResultLocked(
+ private AndroidPackage commitReconciledScanResultLocked(
@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
final InstallRequest request = reconciledPkg.mInstallRequest;
// TODO(b/135203078): Move this even further away
@@ -731,8 +726,9 @@
}
// TODO(b/278553670) Store archive state for the user.
boolean isArchived = (pkgSetting.getPkg() == null);
- mPm.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, pkgSetting, userId,
- isArchived, DataLoaderType.NONE);
+ mBroadcastHelper.sendPackageAddedForUser(mPm.snapshotComputer(), packageName,
+ pkgSetting, userId, isArchived, DataLoaderType.NONE,
+ mPm.mAppPredictionServicePackage);
synchronized (mPm.mLock) {
mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
}
@@ -998,8 +994,7 @@
return;
}
final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
- final boolean isSdkLibrary = packageToScan.isSdkLibrary();
- if (!isApex && !isSdkLibrary) {
+ if (!isApex) {
createdAppId.put(packageName, optimisticallyRegisterAppId(request));
} else {
request.getScannedPackageSetting().setAppId(Process.INVALID_UID);
@@ -1746,7 +1741,7 @@
}
// Update what is removed
- PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm);
+ PackageRemovedInfo removedInfo = new PackageRemovedInfo();
removedInfo.mUid = ps.getAppId();
removedInfo.mRemovedPackage = ps.getPackageName();
removedInfo.mInstallerPackageName =
@@ -2075,8 +2070,6 @@
final InstallRequest installRequest = reconciledPkg.mInstallRequest;
final ParsedPackage parsedPackage = installRequest.getParsedPackage();
final String packageName = parsedPackage.getPackageName();
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
installRequest.onCommitStarted();
if (installRequest.isInstallReplace()) {
@@ -2098,7 +2091,7 @@
allUsers, mPm.mSettings.getPackagesLocked());
if (installRequest.isInstallSystem()) {
// Remove existing system package
- removePackageHelper.removePackage(oldPackage, true);
+ mRemovePackageHelper.removePackage(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage)) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
@@ -2114,7 +2107,7 @@
} else {
try {
// Settings will be written during the call to updateSettingsLI().
- deletePackageHelper.executeDeletePackage(
+ mDeletePackageHelper.executeDeletePackage(
reconciledPkg.mDeletePackageAction, packageName,
true, allUsers, false);
} catch (SystemDeleteException e) {
@@ -2201,12 +2194,19 @@
final String installerPackageName = installRequest.getInstallerPackageName();
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
+ final int userId = installRequest.getUserId();
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
+ && !mPm.mUserManager.exists(userId)) {
+ installRequest.setError(PackageManagerException.ofInternalError(
+ "User " + userId + " doesn't exist or has been removed",
+ PackageManagerException.INTERNAL_ERROR_MISSING_USER));
+ return;
+ }
synchronized (mPm.mLock) {
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- final int userId = installRequest.getUserId();
if (ps != null) {
if (ps.isSystem()) {
if (DEBUG_INSTALL) {
@@ -2797,24 +2797,21 @@
final Computer snapshot = mPm.snapshotComputer();
// Send broadcasts
for (int i = 0; i < numBroadcasts; i++) {
- mPm.sendPackageChangedBroadcast(snapshot, packages[i], true /* dontKillApp */,
- components[i], uids[i], null /* reason */);
+ mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i],
+ true /* dontKillApp */, components[i], uids[i], null /* reason */);
}
}
void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) {
final boolean killApp =
(request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0;
- final boolean virtualPreload =
- ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- final String installerPackage = request.getInstallerPackageName();
- final int dataLoaderType = request.getDataLoaderType();
final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED;
final boolean update = request.isUpdate();
final boolean archived = request.isArchived();
final String packageName = request.getName();
+ final Computer snapshot = mPm.snapshotComputer();
final PackageStateInternal pkgSetting =
- succeeded ? mPm.snapshotComputer().getPackageStateInternal(packageName) : null;
+ succeeded ? snapshot.getPackageStateInternal(packageName) : null;
final boolean removedBeforeUpdate = (pkgSetting == null)
|| (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
request.getPkg().getPath()));
@@ -2835,208 +2832,22 @@
// Clear the uid cache after we installed a new package.
mPm.mPerUidReadTimeoutsCache = null;
- // Send the removed broadcasts
- if (request.getRemovedInfo() != null) {
- if (request.getRemovedInfo().mIsExternal) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final String[] pkgNames = new String[]{
- request.getRemovedInfo().mRemovedPackage};
- final int[] uids = new int[]{request.getRemovedInfo().mUid};
- mPm.notifyResourcesChanged(false /* mediaStatus */,
- true /* replacing */, pkgNames, uids);
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
- false /* mediaStatus */, true /* replacing */, pkgNames, uids);
- }
- request.getRemovedInfo().sendPackageRemovedBroadcasts(
- killApp, false /*removedBySystem*/, false /*isArchived*/);
- }
-
- final String installerPackageName =
- request.getInstallerPackageName() != null
- ? request.getInstallerPackageName()
- : request.getRemovedInfo() != null
- ? request.getRemovedInfo().mInstallerPackageName
- : null;
-
mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(),
request.getNewUsers());
request.populateBroadcastUsers();
final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
- final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
- final int[] updateUserIds = request.getUpdateBroadcastUserIds();
- final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
- Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_UID, request.getAppId());
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- if (archived) {
- extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
- }
- extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-
- // If a package is a static shared library, then only the installer of the package
- // should get the broadcast.
- if (installerPackageName != null
- && request.getPkg().getStaticSharedLibraryName() != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- request.getNewUsers(), null /* instantUserIds*/,
- null /* broadcastAllowList */, null);
- }
-
- // Send installed broadcasts if the package is not a static shared lib.
if (request.getPkg().getStaticSharedLibraryName() == null) {
mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
-
- // Send PACKAGE_ADDED broadcast for users that see the package for the first time
- // sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(request.getAppId());
- boolean isSystem = request.isInstallSystem();
- mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), packageName,
- isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId,
- firstUserIds, firstInstantUserIds, archived, dataLoaderType);
-
- // Send PACKAGE_ADDED broadcast for users that don't see
- // the package for the first time
-
- // Send to all running apps.
- final SparseArray<int[]> newBroadcastAllowList;
- synchronized (mPm.mLock) {
- final Computer snapshot = mPm.snapshotComputer();
- newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(snapshot,
- snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
- updateUserIds, mPm.mSettings.getPackagesLocked());
- }
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, newBroadcastAllowList, null);
- // Send to the installer, even if it's not running.
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // Send to PermissionController for all update users, even if it may not be running
- // for some users
- if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // Notify required verifier(s) that are not the installer of record for the package.
- for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
- if (verifierPackageName != null && !verifierPackageName.equals(
- installerPackageName)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- verifierPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */,
- null);
- }
- }
- // If package installer is defined, notify package installer about new
- // app installed
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
- mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
- firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
-
- // Send replaced for users that don't see the package for the first time
- if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds,
- request.getRemovedInfo().mBroadcastAllowList, null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
- }
- for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
- if (verifierPackageName != null && !verifierPackageName.equals(
- installerPackageName)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/, verifierPackageName,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/, null);
- }
- }
- mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null /*package*/, null /*extras*/, 0 /*flags*/,
- packageName /*targetPackage*/,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/,
- mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
- REASON_PACKAGE_REPLACED).toBundle());
- } else if (launchedForRestore && !request.isInstallSystem()) {
- // First-install and we did a restore, so we're responsible for the
- // first-launch broadcast.
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Post-restore of " + packageName
- + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
- }
- mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
- firstUserIds, firstInstantUserIds);
- }
-
- // Send broadcast package appeared if external for all users
- if (request.getPkg().isExternalStorage()) {
- if (!update) {
- final StorageManager storageManager =
- mInjector.getSystemService(StorageManager.class);
- VolumeInfo volume =
- storageManager.findVolumeByUuid(
- StorageManager.convert(
- request.getPkg().getVolumeUuid()).toString());
- int packageExternalStorageType =
- PackageManagerServiceUtils.getPackageExternalStorageType(volume,
- request.getPkg().isExternalStorage());
- // If the package was installed externally, log it.
- if (packageExternalStorageType != StorageEnums.UNKNOWN) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
- packageExternalStorageType, packageName);
- }
- }
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + request.getPkg() + " is external");
- }
- if (!archived) {
- final String[] pkgNames = new String[]{packageName};
- final int[] uids = new int[]{request.getPkg().getUid()};
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
- true /* mediaStatus */, true /* replacing */, pkgNames, uids);
- mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */,
- pkgNames, uids);
- }
- }
- } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib
- // No need to kill consumers if it's installation of new version static shared lib.
- final Computer snapshot = mPm.snapshotComputer();
- final boolean dontKillApp = !update
- && request.getPkg().getStaticSharedLibraryName() != null;
- for (int i = 0; i < request.getLibraryConsumers().size(); i++) {
- AndroidPackage pkg = request.getLibraryConsumers().get(i);
- // send broadcast that all consumers of the static shared library have changed
- mPm.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), dontKillApp,
- new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
- pkg.getUid(), null);
- }
}
+ mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName,
+ mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages,
+ mPm.mRequiredInstallerPackage,
+ /* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
+
+
// Work that needs to happen on first install within each user
if (firstUserIds.length > 0) {
for (int userId : firstUserIds) {
@@ -3075,7 +2886,6 @@
}
if (!archived) {
- final Computer snapshot = mPm.snapshotComputer();
// Notify DexManager that the package was installed for new users.
// The updated users should already be indexed and the package code paths
// should not change.
@@ -3091,7 +2901,7 @@
}
} else {
// Now send PACKAGE_REMOVED + EXTRA_REPLACING broadcast.
- final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo info = new PackageRemovedInfo();
info.mRemovedPackage = packageName;
info.mInstallerPackageName = request.getInstallerPackageName();
info.mRemovedUsers = firstUserIds;
@@ -3100,8 +2910,8 @@
info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode();
info.mRemovedForAllUsers = true;
- info.sendPackageRemovedBroadcasts(false /*killApp*/,
- false /*removedBySystem*/, true /*isArchived*/);
+ mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm,
+ false /*killApp*/, false /*removedBySystem*/, true /*isArchived*/);
}
}
@@ -3292,15 +3102,14 @@
synchronized (mPm.mLock) {
mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
}
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
- removePackageHelper.removePackage(stubPkg, true /*chatty*/);
+ mRemovePackageHelper.removePackage(stubPkg, true /*chatty*/);
try {
return initPackageTracedLI(scanFile, parseFlags, scanFlags);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
// Remove the failed install
- removePackageHelper.removeCodePath(scanFile);
+ mRemovePackageHelper.removeCodePath(scanFile);
throw e;
}
}
@@ -3343,7 +3152,7 @@
if (!dstCodePath.exists()) {
return null;
}
- new RemovePackageHelper(mPm).removeCodePath(dstCodePath);
+ mRemovePackageHelper.removeCodePath(dstCodePath);
return null;
}
@@ -4299,8 +4108,8 @@
parsedPackage.getPackageName(), UserHandle.USER_ALL,
"scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER,
null /* request */)) {
- DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mDeletePackageHelper.deletePackageLIF(
+ parsedPackage.getPackageName(), null, true,
mPm.mUserManager.getUserIds(), 0, null, false);
}
} else if (newPkgVersionGreater || newSharedUserSetting) {
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index fe6a8a1..ca8dc29 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -94,8 +94,6 @@
private final UserHandle mUser;
@NonNull
final PackageManagerService mPm;
- final InstallPackageHelper mInstallPackageHelper;
- final RemovePackageHelper mRemovePackageHelper;
final boolean mIsInherit;
final int mSessionId;
final int mRequireUserAction;
@@ -108,8 +106,6 @@
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mObserver = observer;
@@ -142,8 +138,6 @@
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mMoveInfo = null;
mInstallReason = fixUpInstallReason(
@@ -242,7 +236,7 @@
// state can change within this delay and hence we need to re-verify certain conditions.
boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
if (isStaged) {
- Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
mRet = ret.first;
if (mRet != INSTALL_SUCCEEDED) {
@@ -540,39 +534,39 @@
}
}
} else {
- mInstallPackageHelper.installPackagesTraced(installRequests);
+ mPm.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
doPostInstall(request);
}
}
for (InstallRequest request : installRequests) {
- mInstallPackageHelper.restoreAndPostInstall(request);
+ mPm.restoreAndPostInstall(request);
}
}
private void doPostInstall(InstallRequest request) {
if (mMoveInfo != null) {
if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
+ mPm.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
} else {
- mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mToUuid,
+ mPm.cleanUpForMoveInstall(mMoveInfo.mToUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
}
} else {
if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.removeCodePath(request.getCodeFile());
+ mPm.removeCodePath(request.getCodeFile());
}
}
}
private void cleanUpForFailedInstall(InstallRequest request) {
if (request.isInstallMove()) {
- mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
+ mPm.cleanUpForMoveInstall(request.getMoveToUuid(),
request.getMovePackageName(), request.getMoveFromCodePath());
} else {
- mRemovePackageHelper.removeCodePath(request.getCodeFile());
+ mPm.removeCodePath(request.getCodeFile());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index b4ca477..4ecbd15 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -60,14 +60,10 @@
*/
final class PackageHandler extends Handler {
private final PackageManagerService mPm;
- private final InstallPackageHelper mInstallPackageHelper;
- private final RemovePackageHelper mRemovePackageHelper;
PackageHandler(Looper looper, PackageManagerService pm) {
super(looper);
mPm = pm;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
}
@Override
@@ -82,7 +78,7 @@
void doHandleMessage(Message msg) {
switch (msg.what) {
case SEND_PENDING_BROADCAST: {
- mInstallPackageHelper.sendPendingBroadcasts();
+ mPm.sendPendingBroadcasts();
break;
}
case POST_INSTALL: {
@@ -96,7 +92,7 @@
request.onInstallCompleted();
request.runPostInstallRunnable();
if (!request.isInstallExistingForUser()) {
- mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
+ mPm.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
@@ -107,7 +103,7 @@
case DEFERRED_NO_KILL_POST_DELETE: {
InstallArgs args = (InstallArgs) msg.obj;
if (args != null) {
- mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
+ mPm.cleanUpResources(args.mCodeFile, args.mInstructionSets);
}
} break;
case DEFERRED_NO_KILL_INSTALL_OBSERVER:
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 95b565d..1bb20b47 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -18,7 +18,9 @@
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
import static android.os.Process.INVALID_UID;
+
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -407,11 +409,10 @@
}
private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
// Clean up orphaned staging directories
for (File stage : stagingDirsToRemove) {
Slog.w(TAG, "Deleting orphan stage " + stage);
- removePackageHelper.removeCodePath(stage);
+ mPm.removeCodePath(stage);
}
}
@@ -1320,9 +1321,8 @@
@Override
public void installExistingPackage(String packageName, int installFlags, int installReason,
IntentSender statusReceiver, int userId, List<String> allowListedPermissions) {
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
- var result = installPackageHelper.installExistingPackageAsUser(packageName, userId,
+ var result = mPm.installExistingPackageAsUser(packageName, userId,
installFlags, installReason, allowListedPermissions, statusReceiver);
int returnCode = result.first;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 54a2e3ad..d0e5f96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2449,14 +2449,13 @@
}
private void onSystemDataLoaderUnrecoverable() {
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName)) {
// The package has not been installed.
return;
}
mHandler.post(() -> {
- if (deletePackageHelper.deletePackageX(packageName,
+ if (mPm.deletePackageX(packageName,
PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/)
!= PackageManager.DELETE_SUCCEEDED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index dea6659..d69737a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -63,6 +63,7 @@
public static final int INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS = -35;
public static final int INTERNAL_ERROR_APEX_NOT_DIRECTORY = -36;
public static final int INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE = -37;
+ public static final int INTERNAL_ERROR_MISSING_USER = -38;
@IntDef(prefix = { "INTERNAL_ERROR_" }, value = {
INTERNAL_ERROR_NATIVE_LIBRARY_COPY,
@@ -101,7 +102,8 @@
INTERNAL_ERROR_STATIC_SHARED_LIB_PROTECTED_BROADCAST,
INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS,
INTERNAL_ERROR_APEX_NOT_DIRECTORY,
- INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE
+ INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE,
+ INTERNAL_ERROR_MISSING_USER
})
@Retention(RetentionPolicy.SOURCE)
public @interface InternalErrorCode {}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 33cb85c..ddc8369 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -70,7 +70,6 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -98,6 +97,7 @@
import android.content.pm.InstantAppRequest;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -115,6 +115,7 @@
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.PackageLite;
@@ -985,7 +986,7 @@
private final DeletePackageHelper mDeletePackageHelper;
private final InitAppsHelper mInitAppsHelper;
private final AppDataHelper mAppDataHelper;
- private final InstallPackageHelper mInstallPackageHelper;
+ @NonNull private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
@@ -1715,7 +1716,8 @@
(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
context),
- (i, pm) -> new UpdateOwnershipHelper());
+ (i, pm) -> new UpdateOwnershipHelper(),
+ (i, pm) -> new PackageMonitorCallbackHelper());
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -2067,17 +2069,19 @@
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
mBroadcastHelper = new BroadcastHelper(mInjector);
- mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector);
+ mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
mAppDataHelper = new AppDataHelper(this);
- mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
- mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper);
+ mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper);
+ mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
+ mBroadcastHelper);
+ mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper,
+ mDeletePackageHelper, mBroadcastHelper);
mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
mInjector.getUserManagerInternal(), mDeletePackageHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
- mPreferredActivityHelper = new PreferredActivityHelper(this);
+ mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper);
mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
injector.getCompatibility(), mUserManager, mDomainVerificationManager,
mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
@@ -2085,7 +2089,7 @@
mDexOptHelper = new DexOptHelper(this);
mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager,
mBroadcastHelper, mProtectedPackages);
- mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper,
+ mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
mSuspendPackageHelper);
mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
mRemovePackageHelper);
@@ -3078,38 +3082,6 @@
}
@Override
- public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
- final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
- final int[] userIds, int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList,
- @Nullable Bundle bOptions) {
- mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags,
- targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
- null /* filterExtrasForReceiver */, bOptions));
- if (targetPkg == null) {
- // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
- // many times to different targets, e.g. installer app, permission controller, other
- // registered apps. We should filter it to avoid calling back many times for the same
- // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
- // installer app or null for registered apps. The callback only need to send back to the
- // registered apps so we check the null condition here.
- notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
- }
- }
-
- void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
- mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
- instantUserIds, broadcastAllowList);
- }
-
- void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
- @NonNull String[] pkgNames, @NonNull int[] uids) {
- mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
- uids);
- }
-
- @Override
public void notifyPackageAdded(String packageName, int uid) {
mPackageObserverHelper.notifyAdded(packageName, uid);
}
@@ -3125,64 +3097,6 @@
UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName);
}
- void sendPackageAddedForUser(@NonNull Computer snapshot, String packageName,
- @NonNull PackageStateInternal packageState, int userId, boolean isArchived,
- int dataLoaderType) {
- final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
- final boolean isSystem = packageState.isSystem();
- final boolean isInstantApp = userState.isInstantApp();
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
- false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
- isArchived, dataLoaderType);
-
- // Send a session commit broadcast
- final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
- info.installReason = userState.getInstallReason();
- info.appPackageName = packageName;
- sendSessionCommitBroadcast(info, userId);
- }
-
- @Override
- public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootCompleted, boolean includeStopped, @AppIdInt int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType) {
- if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
- return;
- }
- SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
- snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
- userIds, snapshot.getPackageStates());
- mHandler.post(
- () -> mBroadcastHelper.sendPackageAddedForNewUsers(packageName, appId, userIds,
- instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
- mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
- instantUserIds, isArchived, dataLoaderType, broadcastAllowList);
- if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
- mHandler.post(() -> {
- for (int userId : userIds) {
- mBroadcastHelper.sendBootCompletedBroadcastToSystemApp(
- packageName, includeStopped, userId);
- }
- }
- );
- }
- }
-
- private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
- int userId) {
- final PackageRemovedInfo info = new PackageRemovedInfo(this);
- info.mRemovedPackage = packageName;
- info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
- info.mRemovedUsers = new int[] {userId};
- info.mBroadcastUsers = new int[] {userId};
- info.mUid = UserHandle.getUid(userId, packageState.getAppId());
- info.mRemovedPackageVersionCode = packageState.getVersionCode();
- info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/,
- false /*isArchived*/);
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -3505,11 +3419,6 @@
}
}
- void postPreferredActivityChangedBroadcast(int userId) {
- mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
- }
-
-
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
void clearPackagePreferredActivitiesLPw(String packageName,
@@ -3609,17 +3518,8 @@
}
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
- UserManagerService ums = UserManagerService.getInstance();
- if (ums == null || sessionInfo.isStaged()) {
- return;
- }
- final UserInfo parent = ums.getProfileParent(userId);
- final int launcherUid = (parent != null) ? parent.id : userId;
- // TODO: Should this snapshot be moved further up?
- final ComponentName launcherComponent = snapshotComputer()
- .getDefaultHomeActivity(launcherUid);
- mBroadcastHelper.sendSessionCommitBroadcast(sessionInfo, userId, launcherUid,
- launcherComponent, mAppPredictionServicePackage);
+ mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId,
+ mAppPredictionServicePackage);
}
private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
@@ -3988,7 +3888,7 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
+ if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
@@ -4070,8 +3970,8 @@
final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
final int packageUid = UserHandle.getUid(
userId, pkgSettings.get(packageName).getAppId());
- sendPackageChangedBroadcast(newSnapshot, packageName, false /* dontKillApp */,
- components, packageUid, null /* reason */);
+ mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName,
+ false /* dontKillApp */, components, packageUid, null /* reason */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -4147,27 +4047,6 @@
}
}
- void sendPackageChangedBroadcast(@NonNull Computer snapshot, String packageName,
- boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
- PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
- Process.SYSTEM_UID);
- if (setting == null) {
- return;
- }
- final int userId = UserHandle.getUserId(packageUid);
- final boolean isInstantApp =
- snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- final SparseArray<int[]> broadcastAllowList =
- isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
- mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast(
- packageName, dontKillApp, componentNames, packageUid, reason, userIds,
- instantUserIds, broadcastAllowList));
- mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
- packageUid, reason, userIds, instantUserIds, broadcastAllowList);
- }
-
/**
* Used by SystemServer
*/
@@ -4312,7 +4191,7 @@
if (pkg == null) {
return;
}
- sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+ mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
true /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(),
@@ -5294,7 +5173,7 @@
throw new SecurityException("Calling package " + packageName
+ " does not belong to calling uid " + callingUid);
}
- return mSuspendPackageHelper
+ return SuspendPackageHelper
.getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid);
}
@@ -5857,10 +5736,14 @@
if (hidden) {
killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg",
ApplicationExitInfo.REASON_OTHER);
- sendApplicationHiddenForUser(packageName, newPackageState, userId);
+ mBroadcastHelper.sendApplicationHiddenForUser(
+ packageName, newPackageState, userId,
+ /* packageSender= */ PackageManagerService.this);
} else {
- sendPackageAddedForUser(newSnapshot, packageName, newPackageState, userId,
- false /* isArchived */, DataLoaderType.NONE);
+ mBroadcastHelper.sendPackageAddedForUser(
+ newSnapshot, packageName, newPackageState, userId,
+ false /* isArchived */, DataLoaderType.NONE,
+ mAppPredictionServicePackage);
}
scheduleWritePackageRestrictions(userId);
@@ -7929,4 +7812,75 @@
}
}
}
+
+ void removeCodePath(@Nullable File codePath) {
+ mRemovePackageHelper.removeCodePath(codePath);
+ }
+
+ void cleanUpResources(@Nullable File codeFile, @Nullable String[] instructionSets) {
+ mRemovePackageHelper.cleanUpResources(codeFile, instructionSets);
+ }
+
+ void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) {
+ mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath);
+ }
+
+ void sendPendingBroadcasts() {
+ mInstallPackageHelper.sendPendingBroadcasts();
+ }
+
+ void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) {
+ mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore);
+ }
+
+ Pair<Integer, IntentSender> installExistingPackageAsUser(
+ @Nullable String packageName,
+ @UserIdInt int userId, @PackageManager.InstallFlags int installFlags,
+ @PackageManager.InstallReason int installReason,
+ @Nullable List<String> allowlistedRestrictedPermissions,
+ @Nullable IntentSender intentSender) {
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ installReason, allowlistedRestrictedPermissions, intentSender);
+ }
+ AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
+ throws PackageManagerException {
+ return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags);
+ }
+
+ void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action,
+ @NonNull int[] allUserHandles,
+ boolean writeSettings) throws SystemDeleteException {
+ mInstallPackageHelper.restoreDisabledSystemPackageLIF(
+ action, allUserHandles, writeSettings);
+ }
+ boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg,
+ @NonNull PackageSetting stubPs) {
+ return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs);
+ }
+
+ void installPackagesTraced(List<InstallRequest> requests) {
+ mInstallPackageHelper.installPackagesTraced(requests);
+ }
+
+ void restoreAndPostInstall(InstallRequest request) {
+ mInstallPackageHelper.restoreAndPostInstall(request);
+ }
+
+ Pair<Integer, String> verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode,
+ int installFlags) {
+ return mInstallPackageHelper.verifyReplacingVersionCode(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
+ int getUidForVerifier(VerifierInfo verifierInfo) {
+ return mInstallPackageHelper.getUidForVerifier(verifierInfo);
+ }
+
+ int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
+ boolean removedBySystem) {
+ return mDeletePackageHelper.deletePackageX(packageName,
+ PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
+ PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 0c2e082..5b770aab 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -146,6 +146,7 @@
private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer;
private final Singleton<CrossProfileIntentFilterHelper> mCrossProfileIntentFilterHelperProducer;
private final Singleton<UpdateOwnershipHelper> mUpdateOwnershipHelperProducer;
+ private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -186,7 +187,8 @@
Producer<IBackupManager> iBackupManager,
Producer<SharedLibrariesImpl> sharedLibrariesProducer,
Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
- Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
+ Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
+ Producer<PackageMonitorCallbackHelper> packageMonitorCallbackHelper) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -242,6 +244,7 @@
mCrossProfileIntentFilterHelperProducer = new Singleton<>(
crossProfileIntentFilterHelperProducer);
mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
+ mPackageMonitorCallbackHelper = new Singleton<>(packageMonitorCallbackHelper);
}
/**
@@ -431,6 +434,10 @@
return mUpdateOwnershipHelperProducer.get(this, mPackageManager);
}
+ public PackageMonitorCallbackHelper getPackageMonitorCallbackHelper() {
+ return mPackageMonitorCallbackHelper.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index bb3bf53..b8c2b86 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -52,12 +52,6 @@
private final Object mLock = new Object();
final IActivityManager mActivityManager = ActivityManager.getService();
- final Handler mHandler;
-
- PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) {
- mHandler = injector.getHandler();
- }
-
@NonNull
@GuardedBy("mLock")
private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
@@ -100,7 +94,8 @@
public void notifyPackageAddedForNewUsers(String packageName,
@AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds,
- boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList) {
+ boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList,
+ @NonNull Handler handler) {
Bundle extras = new Bundle(2);
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
final int uid = UserHandle.getUid(
@@ -111,11 +106,11 @@
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
- userIds /* userIds */, instantUserIds, broadcastAllowList);
+ userIds /* userIds */, instantUserIds, broadcastAllowList, handler);
}
public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
- @NonNull String[] pkgNames, @NonNull int[] uids) {
+ @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler) {
Bundle extras = new Bundle();
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
@@ -125,12 +120,12 @@
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, handler);
}
public void notifyPackageChanged(String packageName, boolean dontKillApp,
ArrayList<String> componentNames, int packageUid, String reason, int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler) {
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
String[] nameList = new String[componentNames.size()];
@@ -142,11 +137,12 @@
extras.putString(Intent.EXTRA_REASON, reason);
}
notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
- instantUserIds, broadcastAllowList);
+ instantUserIds, broadcastAllowList, handler);
}
public void notifyPackageMonitor(String action, String pkg, Bundle extras,
- int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
+ Handler handler) {
if (!isAllowedCallbackAction(action)) {
return;
}
@@ -160,9 +156,10 @@
}
if (ArrayUtils.isEmpty(instantUserIds)) {
- doNotifyCallbacks(action, pkg, extras, resolvedUserIds, broadcastAllowList);
+ doNotifyCallbacks(
+ action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
} else {
- doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList);
+ doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler);
}
} catch (RemoteException e) {
// do nothing
@@ -181,7 +178,7 @@
}
private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds,
- SparseArray<int[]> broadcastAllowList) {
+ SparseArray<int[]> broadcastAllowList, Handler handler) {
RemoteCallbackList<IRemoteCallback> callbacks;
synchronized (mLock) {
callbacks = mCallbacks;
@@ -202,7 +199,7 @@
final int[] allowUids =
broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{};
- mHandler.post(() -> callbacks.broadcast((callback, user) -> {
+ handler.post(() -> callbacks.broadcast((callback, user) -> {
RegisterUser registerUser = (RegisterUser) user;
if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
!= userId)) {
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 9f02542..7ee1772 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -16,24 +16,12 @@
package com.android.server.pm;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
-import android.annotation.NonNull;
-import android.app.ActivityManagerInternal;
-import android.app.BroadcastOptions;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.PowerExemptionManager;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
final class PackageRemovedInfo {
- final PackageSender mPackageSender;
String mRemovedPackage;
String mInstallerPackageName;
int mUid = -1;
@@ -58,116 +46,6 @@
InstallArgs mArgs = null;
private static final int[] EMPTY_INT_ARRAY = new int[0];
- PackageRemovedInfo(PackageSender packageSender) {
- mPackageSender = packageSender;
- }
-
- void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem,
- boolean isArchived) {
- sendPackageRemovedBroadcastInternal(killApp, removedBySystem, isArchived);
- }
-
- void sendSystemPackageUpdatedBroadcasts() {
- if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
- }
- }
-
- private void sendSystemPackageUpdatedBroadcastsInternal() {
- Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
- 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- if (mInstallerPackageName != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- }
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- mRemovedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
- }
-
- private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
- @PowerExemptionManager.ReasonCode int reasonCode) {
- long duration = 10_000;
- final ActivityManagerInternal amInternal =
- LocalServices.getService(ActivityManagerInternal.class);
- if (amInternal != null) {
- duration = amInternal.getBootTimeTempAllowListDuration();
- }
- final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
- bOptions.setTemporaryAppAllowlist(duration,
- TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- reasonCode, "");
- return bOptions;
- }
-
- private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem,
- boolean isArchived) {
- Bundle extras = new Bundle();
- final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
- extras.putInt(Intent.EXTRA_UID, removedUid);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
- extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, mIsRemovedPackageSystemUpdate);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
- extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- final boolean isReplace = mIsUpdate || mIsRemovedPackageSystemUpdate;
- if (isReplace || isArchived) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- if (isArchived) {
- extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
- }
- extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
-
- // Send PACKAGE_REMOVED broadcast to the respective installer.
- if (mRemovedPackage != null && mInstallerPackageName != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, mBroadcastUsers, mInstantUserIds, null, null);
- }
- if (mIsStaticSharedLib) {
- // When uninstalling static shared libraries, only the package's installer needs to be
- // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
- return;
- }
- if (mRemovedPackage != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- mRemovedPackage, extras, 0, null /*targetPackage*/, null,
- mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
- mRemovedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
- null /*finishedReceiver*/, mBroadcastUsers, mInstantUserIds,
- mBroadcastAllowList, null /*bOptions*/);
- if (mDataRemoved && !mIsRemovedPackageSystemUpdate) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
- mRemovedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
- null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- mPackageSender.notifyPackageRemoved(mRemovedPackage, removedUid);
- }
- }
- if (mRemovedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (isReplace) {
- extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
- }
-
- mPackageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
- null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- null, null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- }
- }
-
public void populateBroadcastUsers(PackageSetting deletedPackageSetting) {
if (mRemovedUsers == null) {
mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/PackageSender.java b/services/core/java/com/android/server/pm/PackageSender.java
index 82e1d5f3..db83f59 100644
--- a/services/core/java/com/android/server/pm/PackageSender.java
+++ b/services/core/java/com/android/server/pm/PackageSender.java
@@ -16,24 +16,7 @@
package com.android.server.pm;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IIntentReceiver;
-import android.os.Bundle;
-import android.util.SparseArray;
-
interface PackageSender {
- /**
- * @param userIds User IDs where the action occurred on a full application
- * @param instantUserIds User IDs where the action occurred on an instant application
- */
- void sendPackageBroadcast(String action, String pkg,
- Bundle extras, int flags, String targetPkg,
- IIntentReceiver finishedReceiver, int[] userIds, int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions);
- void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType);
void notifyPackageAdded(String packageName, int uid);
void notifyPackageChanged(String packageName, int uid);
void notifyPackageRemoved(String packageName, int uid);
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 571aab4..41d2aeb 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -69,10 +69,12 @@
private static final String TAG_DEFAULT_APPS = "da";
private final PackageManagerService mPm;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- PreferredActivityHelper(PackageManagerService pm) {
+ PreferredActivityHelper(PackageManagerService pm, BroadcastHelper broadcastHelper) {
mPm = pm;
+ mBroadcastHelper = broadcastHelper;
}
private ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot, Intent intent,
@@ -120,7 +122,7 @@
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -167,7 +169,7 @@
return mPm.setActiveLauncherPackage(packageName, userId,
successful -> {
if (successful) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
});
}
@@ -215,7 +217,7 @@
}
// Re-snapshot after mLock
if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId))) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
}
@@ -411,7 +413,7 @@
if (isHomeFilter(filter)) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
}
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
@@ -426,7 +428,7 @@
}
if (changed) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -443,7 +445,7 @@
}
if (changed) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -616,7 +618,7 @@
mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
}
if (changedUsers.size() > 0) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
synchronized (mPm.mLock) {
mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index d989c90..b055a3f 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -22,6 +22,7 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -69,9 +70,11 @@
private final PermissionManagerServiceInternal mPermissionManager;
private final SharedLibrariesImpl mSharedLibraries;
private final AppDataHelper mAppDataHelper;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+ RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mIncrementalManager = mPm.mInjector.getIncrementalManager();
mInstaller = mPm.mInjector.getInstaller();
@@ -79,10 +82,7 @@
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl();
mAppDataHelper = appDataHelper;
- }
-
- RemovePackageHelper(PackageManagerService pm) {
- this(pm, new AppDataHelper(pm));
+ mBroadcastHelper = broadcastHelper;
}
public void removeCodePath(File codePath) {
@@ -265,7 +265,8 @@
final List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
- final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
+ final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm,
+ mBroadcastHelper);
final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
: new int[] {userId};
for (int nextUserId : userIds) {
@@ -395,13 +396,13 @@
}
if (changedUsers.size() > 0) {
final PreferredActivityHelper preferredActivityHelper =
- new PreferredActivityHelper(mPm);
+ new PreferredActivityHelper(mPm, mBroadcastHelper);
preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(),
changedUsers);
- mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
}
} else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate
- && outInfo.mRemovedUsers != null) {
+ && outInfo.mRemovedUsers != null && !outInfo.mIsExternal) {
// For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false
// for affected users. This does not apply to app updates where the old apk is replaced
// but the old data remains.
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 0ea45c4..e993d9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -372,6 +372,7 @@
// Extract Icon and update the icon res ID and the bitmap path.
s.saveIconAndFixUpShortcutLocked(this, newShortcut);
s.fixUpShortcutResourceNamesAndValues(newShortcut);
+ ensureShortcutCountBeforePush();
saveShortcut(newShortcut);
}
@@ -426,7 +427,6 @@
@NonNull List<ShortcutInfo> changedShortcuts) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"pushDynamicShortcuts() cannot publish disabled shortcuts");
- ensureShortcutCountBeforePush();
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index db5b9b1..c725cdc 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -147,7 +147,6 @@
final Settings.VersionInfo ver;
final List<? extends PackageStateInternal> packages;
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
synchronized (mPm.mLock) {
ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
@@ -160,7 +159,7 @@
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
- pkg = installPackageHelper.initPackageTracedLI(
+ pkg = mPm.initPackageTracedLI(
ps.getPath(), parseFlags, SCAN_INITIAL);
loaded.add(pkg);
@@ -228,7 +227,8 @@
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- sendResourcesChangedBroadcast(true /* mediaStatus */, false /* replacing */, loaded);
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ true /* mediaStatus */, false /* replacing */, loaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.add(vol.getId());
}
@@ -256,7 +256,7 @@
final AndroidPackage pkg = ps.getPkg();
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo();
try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
UserHandle.USER_ALL, deleteFlags,
@@ -280,7 +280,8 @@
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- sendResourcesChangedBroadcast(false /* mediaStatus */, false /* replacing */, unloaded);
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ false /* mediaStatus */, false /* replacing */, unloaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.remove(vol.getId());
}
@@ -295,21 +296,6 @@
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<AndroidPackage> packages) {
- final int size = packages.size();
- final String[] packageNames = new String[size];
- final int[] packageUids = new int[size];
- for (int i = 0; i < size; i++) {
- final AndroidPackage pkg = packages.get(i);
- packageNames[i] = pkg.getPackageName();
- packageUids[i] = pkg.getUid();
- }
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus,
- replacing, packageNames, packageUids);
- mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
- }
-
/**
* Examine all apps present on given mounted volume, and destroy apps that
* aren't expected, either due to uninstallation or reinstallation on
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index ddb045d..29d99a73 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -26,17 +26,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.SuspendDialogInfo;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
@@ -47,7 +43,6 @@
import android.util.IntArray;
import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
@@ -207,19 +202,22 @@
}
});
+ final Computer newSnapshot = mPm.snapshotComputer();
if (!notifyPackagesList.isEmpty()) {
final String[] changedPackages =
notifyPackagesList.toArray(new String[0]);
- sendPackagesSuspendedForUser(
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
changedPackages, notifyUids.toArray(), quarantined, userId);
- sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
+ suspended, userId);
mPm.scheduleWritePackageRestrictions(userId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
userId);
}
@@ -269,8 +267,10 @@
* @return The app extras of the suspended package.
*/
@Nullable
- Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, @NonNull String packageName,
- int userId, int callingUid) {
+ static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ int userId,
+ int callingUid) {
final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
if (ps == null) {
return null;
@@ -299,7 +299,7 @@
* suspensions will be removed.
* @param userId The user for which the changes are taking place.
*/
- void removeSuspensionsBySuspendingPackage(@NonNull Computer computer,
+ void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
@NonNull String[] packagesToChange,
@NonNull Predicate<String> suspendingPackagePredicate, int userId) {
final List<String> unsuspendedPackages = new ArrayList<>();
@@ -307,7 +307,7 @@
final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
for (String packageName : packagesToChange) {
final PackageStateInternal packageState =
- computer.getPackageStateInternal(packageName);
+ snapshot.getPackageStateInternal(packageName);
final PackageUserStateInternal packageUserState = packageState == null
? null : packageState.getUserStateOrDefault(userId);
if (packageUserState == null || !packageUserState.isSuspended()) {
@@ -350,11 +350,14 @@
});
mPm.scheduleWritePackageRestrictions(userId);
+ final Computer newSnapshot = mPm.snapshotComputer();
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
- sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
+ false, userId);
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ Intent.ACTION_PACKAGES_UNSUSPENDED,
packageArray, unsuspendedUids.toArray(), false, userId);
}
}
@@ -610,38 +613,6 @@
}
/**
- * Send broadcast intents for packages suspension changes.
- *
- * @param intent The action name of the suspension intent.
- * @param pkgList The names of packages which have suspension changes.
- * @param uidList The uids of packages which have suspension changes.
- * @param userId The user where packages reside.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
- @NonNull int[] uidList, boolean quarantined, int userId) {
- final Handler handler = mInjector.getHandler();
- final Bundle extras = new Bundle(3);
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- if (quarantined) {
- extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
- }
- final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
- final Bundle options = new BroadcastOptions()
- .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
- .toBundle();
- handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
- extras, flags, null /* targetPkg */, null /* finishedReceiver */,
- new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
- (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- mPm.snapshotComputer(), callingUid, intentExtras),
- options));
- mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
- null /* instantUserIds */, null /* broadcastAllowList */);
- }
-
- /**
* Suspends packages on behalf of an admin.
*
* @return array of packages that are unsuspendable, either because admin is not allowed to
@@ -756,37 +727,4 @@
}
return false;
}
-
- private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
- int userId) {
- final Handler handler = mInjector.getHandler();
- final String action = suspended
- ? Intent.ACTION_MY_PACKAGE_SUSPENDED
- : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
- handler.post(() -> {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
- + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
- return;
- }
- final int[] targetUserIds = new int[] {userId};
- final Computer snapshot = mPm.snapshotComputer();
- for (String packageName : affectedPackages) {
- final Bundle appExtras = suspended
- ? getSuspendedPackageAppExtras(snapshot, packageName, userId, SYSTEM_UID)
- : null;
- final Bundle intentExtras;
- if (appExtras != null) {
- intentExtras = new Bundle(1);
- intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
- } else {
- intentExtras = null;
- }
- mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false, null, null, null);
- }
- });
- }
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 8c73ce8..c6435ae 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -139,7 +139,6 @@
private final UserHandle mUser;
@NonNull
private final PackageManagerService mPm;
- private final InstallPackageHelper mInstallPackageHelper;
VerifyingSession(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
@@ -147,7 +146,6 @@
boolean userActionRequired, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
@@ -181,7 +179,7 @@
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
- Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
setReturnCode(ret.first, ret.second);
if (mRet != INSTALL_SUCCEEDED) {
@@ -729,7 +727,7 @@
continue;
}
- final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo);
+ final int verifierUid = mPm.getUidForVerifier(verifierInfo);
if (verifierUid == -1) {
continue;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dc75a98..dfc9b8b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -198,6 +198,7 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
+import com.android.internal.display.BrightnessUtils;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -217,7 +218,6 @@
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.UiThread;
-import com.android.server.display.BrightnessUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.input.KeyboardMetricsCollector;
import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
@@ -1983,7 +1983,6 @@
public void run() {
if (mPendingHomeKeyEvent != null) {
handleShortPressOnHome(mPendingHomeKeyEvent);
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
}
}
@@ -2028,7 +2027,7 @@
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
|| mPictureInPictureVisible) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
- mPendingHomeKeyEvent = KeyEvent.obtain(event);
+ mPendingHomeKeyEvent = event;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return true;
@@ -2036,11 +2035,7 @@
}
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent shortPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleShortPressOnHome(shortPressEvent);
- shortPressEvent.recycle();
- });
+ mHandler.post(() -> handleShortPressOnHome(event));
return true;
}
@@ -2067,14 +2062,9 @@
if (repeatCount == 0) {
mHomePressed = true;
if (mPendingHomeKeyEvent != null) {
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- final KeyEvent doublePressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleDoubleTapOnHome(doublePressEvent);
- doublePressEvent.recycle();
- });
+ mHandler.post(() -> handleDoubleTapOnHome(event));
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -2084,11 +2074,7 @@
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent longPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleLongPressOnHome(longPressEvent);
- longPressEvent.recycle();
- });
+ mHandler.post(() -> handleLongPressOnHome(event));
}
}
return true;
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 96f4a01..c2666f6 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -297,7 +297,8 @@
return;
}
- for (ClientState clientState : mClients.values()) {
+
+ for (ClientState clientState : mClients.values().toArray(new ClientState[0])) {
tryRespondWithError(
clientState.mDelegatingListener.mRemoteListener,
SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 40e9c13..88eaafa 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -955,6 +955,17 @@
}
}
+ public void setTiles(String tiles) {
+ enforceStatusBarOrShell();
+
+ if (mBar != null) {
+ try {
+ mBar.setQsTiles(tiles.split(","));
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
public void clickTile(ComponentName component) {
enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index 11a4976d..d6bf02f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -61,6 +61,8 @@
return runAddTile();
case "remove-tile":
return runRemoveTile();
+ case "set-tiles":
+ return runSetTiles();
case "click-tile":
return runClickTile();
case "check-support":
@@ -105,6 +107,11 @@
return 0;
}
+ private int runSetTiles() throws RemoteException {
+ mInterface.setTiles(getNextArgRequired());
+ return 0;
+ }
+
private int runClickTile() throws RemoteException {
mInterface.clickTile(ComponentName.unflattenFromString(getNextArgRequired()));
return 0;
@@ -242,6 +249,9 @@
pw.println(" remove-tile COMPONENT");
pw.println(" Remove a TileService of the specified component");
pw.println("");
+ pw.println(" set-tiles LIST-OF-TILES");
+ pw.println(" Sets the list of tiles as the current Quick Settings tiles");
+ pw.println("");
pw.println(" click-tile COMPONENT");
pw.println(" Click on a TileService of the specified component");
pw.println("");
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f33ecaa..184de58 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -301,9 +301,12 @@
}
// Always update the reordering time when this is called to ensure that the timeout
- // is reset
+ // is reset. Extend this duration when running in tests.
+ final long timeout = ActivityManager.isRunningInUserTestHarness()
+ ? mFreezeTaskListTimeoutMs * 10
+ : mFreezeTaskListTimeoutMs;
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
- mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
+ mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a6d285a..3faf8b9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5241,16 +5241,17 @@
applyForcedPropertiesForDefaultDisplay();
mAnimator.ready();
mDisplayReady = true;
- // Reconfigure all displays to make sure that forced properties and
- // DisplayWindowSettings are applied.
- mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
+ mHasWideColorGamutSupport = queryWideColorGamutSupport();
+ mHasHdrSupport = queryHdrSupport();
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_FAKETOUCH);
+ // Reconfigure all displays to make sure that the forced properties and
+ // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also
+ // affect the Configuration.
+ mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
}
-
- mAtmService.updateConfiguration(null /* request to compute config */);
}
public void systemReady() {
@@ -5258,8 +5259,6 @@
mPolicy.systemReady();
mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
mSnapshotController.systemReady();
- mHasWideColorGamutSupport = queryWideColorGamutSupport();
- mHasHdrSupport = queryHdrSupport();
UiThread.getHandler().post(mSettingsObserver::loadSettings);
IVrManager vrManager = IVrManager.Stub.asInterface(
ServiceManager.getService(Context.VR_SERVICE));
@@ -9647,4 +9646,15 @@
Binder.restoreCallingIdentity(origId);
}
}
+
+ /**
+ * Resets the spatial ordering of recents for testing purposes.
+ */
+ void resetFreezeRecentTaskListReordering() {
+ if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS,
+ "resetFreezeRecentTaskListReordering()")) {
+ throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+ }
+ mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 8fad950..fa9a65f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -142,6 +142,8 @@
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
+ case "reset-freeze-recent-tasks":
+ return runResetFreezeRecentTaskListReordering(pw);
case "shell":
return runWmShellCommand(pw);
default:
@@ -252,6 +254,11 @@
return 0;
}
+ private int runResetFreezeRecentTaskListReordering(PrintWriter pw) throws RemoteException {
+ mInternal.resetFreezeRecentTaskListReordering();
+ return 0;
+ }
+
private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
try {
final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
@@ -1492,6 +1499,8 @@
printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
+ pw.println(" reset-freeze-recent-tasks");
+ pw.println(" Resets the spatial ordering of the recent tasks list");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d0ead14..25e8475 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -21,6 +21,7 @@
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY;
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_CONFLICTING_ADMIN_POLICY;
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_HARDWARE_LIMITATION;
+import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
@@ -51,6 +52,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -63,6 +65,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.utils.Slogf;
import libcore.io.IoUtils;
@@ -117,6 +120,10 @@
* Map containing the current set of admins in each user with active policies.
*/
private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins;
+ private final SparseArray<HashMap<EnforcingAdmin, Integer>> mAdminPolicySize;
+
+ //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
+ private static final int POLICY_SIZE_LIMIT = 99999;
private final DeviceAdminServiceController mDeviceAdminServiceController;
@@ -131,6 +138,7 @@
mLocalPolicies = new SparseArray<>();
mGlobalPolicies = new HashMap<>();
mEnforcingAdmins = new SparseArray<>();
+ mAdminPolicySize = new SparseArray<>();
}
/**
@@ -139,7 +147,6 @@
*
* <p>If {@code skipEnforcePolicy} is true, it sets the policies in the internal data structure
* but doesn't call the enforcing logic.
- *
*/
<V> void setLocalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@@ -152,6 +159,12 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+ policyDefinition, userId)) {
+ return;
+ }
+ }
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -236,6 +249,7 @@
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Set the policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -250,6 +264,7 @@
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Removes any previously set policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -267,6 +282,10 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
+ }
+
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
enforcingAdmin, /* value= */ null, userId, /* skipEnforcePolicy= */ false);
@@ -392,6 +411,7 @@
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Set the policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -407,6 +427,13 @@
Objects.requireNonNull(value);
synchronized (mLock) {
+ PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+ policyDefinition, UserHandle.USER_ALL)) {
+ return;
+ }
+ }
// TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
// that honors the restriction once there's an API available
if (checkFor2gFailure(policyDefinition, enforcingAdmin)) {
@@ -416,8 +443,6 @@
return;
}
- PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
-
boolean policyChanged = globalPolicyState.addPolicy(enforcingAdmin, value);
boolean policyAppliedOnAllUsers = applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
policyDefinition, enforcingAdmin, value, skipEnforcePolicy);
@@ -434,7 +459,7 @@
// TODO(b/285532044): remove hack and handle properly
if (!policyAppliedGlobally
&& policyDefinition.getPolicyKey().getIdentifier().equals(
- USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+ USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
PolicyValue<Set<String>> parsedResolvedValue =
(PolicyValue<Set<String>>) globalPolicyState.getCurrentResolvedPolicy();
@@ -459,6 +484,7 @@
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Removes any previously set policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -472,6 +498,11 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
+
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ decreasePolicySizeForAdmin(policyState, enforcingAdmin);
+ }
+
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
if (policyChanged) {
@@ -687,7 +718,6 @@
* <p>Note that this will always return at most one item for policies that do not require
* additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
* {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
- *
*/
@NonNull
<V> Set<PolicyKey> getLocalPolicyKeysSetByAdmin(
@@ -723,7 +753,6 @@
* <p>Note that this will always return at most one item for policies that do not require
* additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
* {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
- *
*/
@NonNull
<V> Set<PolicyKey> getLocalPolicyKeysSetByAllAdmins(
@@ -964,7 +993,7 @@
EnforcingAdmin callingAdmin,
PolicyDefinition<V> policyDefinition,
int userId) {
- for (EnforcingAdmin admin: policyState.getPoliciesSetByAdmins().keySet()) {
+ for (EnforcingAdmin admin : policyState.getPoliciesSetByAdmins().keySet()) {
// We're sending a separate broadcast for the calling admin with the result.
if (admin.equals(callingAdmin)) {
continue;
@@ -1152,7 +1181,7 @@
try {
if (packageManager.getPackageInfo(packageName, 0, userId) == null
|| packageManager.getActivityInfo(
- policies.get(admin).getValue(), 0, userId) == null) {
+ policies.get(admin).getValue(), 0, userId) == null) {
Slogf.e(TAG, String.format(
"Persistent preferred activity in package %s not found for "
+ "user %d, removing policy for admin",
@@ -1450,6 +1479,97 @@
return false;
}
+ /**
+ * Calculate the size of a policy in bytes
+ */
+
+ private static <V> int sizeOf(PolicyValue<V> value) {
+ try {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(value, /* flags= */ 0);
+
+ parcel.setDataPosition(0);
+
+ byte[] bytes;
+
+ bytes = parcel.marshall();
+ return bytes.length;
+ } catch (Exception e) {
+ Log.e(TAG, "Error calculating size of policy: " + e);
+ return 0;
+ }
+ }
+
+ /**
+ * Checks if the policy already exists and removes the current size to prevent recording the
+ * same policy twice.
+ *
+ * Checks if the new sum of the size of all policies is less than the maximum sum of policies
+ * size per admin and returns true.
+ *
+ * If the policy size limit is reached then send policy result to admin and return false.
+ */
+
+ private <V> boolean handleAdminPolicySizeLimit(PolicyState<V> policyState, EnforcingAdmin admin,
+ PolicyValue<V> value, PolicyDefinition policyDefinition, int userId) {
+ int currentSize = 0;
+ if (mAdminPolicySize.contains(admin.getUserId())
+ && mAdminPolicySize.get(
+ admin.getUserId()).containsKey(admin)) {
+ currentSize = mAdminPolicySize.get(admin.getUserId()).get(admin);
+ }
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ currentSize -= sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
+ }
+ int policySize = sizeOf(value);
+ if (currentSize + policySize < POLICY_SIZE_LIMIT) {
+ increasePolicySizeForAdmin(admin, policySize);
+ return true;
+ } else {
+ sendPolicyResultToAdmin(
+ admin,
+ policyDefinition,
+ RESULT_FAILURE_STORAGE_LIMIT_REACHED,
+ userId);
+ return false;
+ }
+ }
+
+ /**
+ * Increase the int in mAdminPolicySize representing the size of the sum of all
+ * active policies for that admin.
+ */
+
+ private <V> void increasePolicySizeForAdmin(EnforcingAdmin admin, int policySize) {
+ if (!mAdminPolicySize.contains(admin.getUserId())) {
+ mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+ }
+ if (!mAdminPolicySize.get(admin.getUserId()).containsKey(admin)) {
+ mAdminPolicySize.get(admin.getUserId()).put(admin, /* size= */ 0);
+ }
+ mAdminPolicySize.get(admin.getUserId()).put(admin,
+ mAdminPolicySize.get(admin.getUserId()).get(admin) + policySize);
+ }
+
+ /**
+ * Decrease the int in mAdminPolicySize representing the size of the sum of all
+ * active policies for that admin.
+ */
+
+ private <V> void decreasePolicySizeForAdmin(PolicyState<V> policyState, EnforcingAdmin admin) {
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ mAdminPolicySize.get(admin.getUserId()).put(admin,
+ mAdminPolicySize.get(admin.getUserId()).get(admin) - sizeOf(
+ policyState.getPoliciesSetByAdmins().get(admin)));
+ }
+ if (mAdminPolicySize.get(admin.getUserId()).get(admin) <= 0) {
+ mAdminPolicySize.get(admin.getUserId()).remove(admin);
+ }
+ if (mAdminPolicySize.get(admin.getUserId()).isEmpty()) {
+ mAdminPolicySize.remove(admin.getUserId());
+ }
+ }
+
@NonNull
private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
synchronized (mLock) {
@@ -1508,11 +1628,13 @@
clear();
write();
}
+
private void clear() {
synchronized (mLock) {
mGlobalPolicies.clear();
mLocalPolicies.clear();
mEnforcingAdmins.clear();
+ mAdminPolicySize.clear();
}
}
@@ -1553,7 +1675,11 @@
private static final String TAG_POLICY_STATE_ENTRY = "policy-state-entry";
private static final String TAG_POLICY_KEY_ENTRY = "policy-key-entry";
private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry";
+ private static final String TAG_ENFORCING_ADMIN_AND_SIZE = "enforcing-admin-and-size";
+ private static final String TAG_ENFORCING_ADMIN = "enforcing-admin";
+ private static final String TAG_POLICY_SUM_SIZE = "policy-sum-size";
private static final String ATTR_USER_ID = "user-id";
+ private static final String ATTR_POLICY_SUM_SIZE = "size";
private final File mFile;
@@ -1595,6 +1721,7 @@
writeLocalPoliciesInner(serializer);
writeGlobalPoliciesInner(serializer);
writeEnforcingAdminsInner(serializer);
+ writeEnforcingAdminSizeInner(serializer);
}
private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
@@ -1652,6 +1779,30 @@
}
}
+ private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
+ throws IOException {
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (mAdminPolicySize != null) {
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ for (EnforcingAdmin admin : mAdminPolicySize.get(
+ userId).keySet()) {
+ serializer.startTag(/* namespace= */ null,
+ TAG_ENFORCING_ADMIN_AND_SIZE);
+ serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ admin.saveToXml(serializer);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+ mAdminPolicySize.get(userId).get(admin));
+ serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
+ }
+ }
+ }
+ }
+ }
+
void readFromFileLocked() {
if (!mFile.exists()) {
Log.d(TAG, "" + mFile + " doesn't exist");
@@ -1689,6 +1840,9 @@
case TAG_ENFORCING_ADMINS_ENTRY:
readEnforcingAdminsInner(parser);
break;
+ case TAG_ENFORCING_ADMIN_AND_SIZE:
+ readEnforcingAdminAndSizeInner(parser);
+ break;
default:
Slogf.wtf(TAG, "Unknown tag " + tag);
}
@@ -1767,5 +1921,37 @@
}
mEnforcingAdmins.get(admin.getUserId()).add(admin);
}
+
+ private void readEnforcingAdminAndSizeInner(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ EnforcingAdmin admin = null;
+ int size = 0;
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ String tag = parser.getName();
+ switch (tag) {
+ case TAG_ENFORCING_ADMIN:
+ admin = EnforcingAdmin.readFromXml(parser);
+ break;
+ case TAG_POLICY_SUM_SIZE:
+ size = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
+ break;
+ default:
+ Slogf.wtf(TAG, "Unknown tag " + tag);
+ }
+ }
+ if (admin == null) {
+ Slogf.wtf(TAG, "Error parsing enforcingAdmins, EnforcingAdmin is null.");
+ return;
+ }
+ if (size <= 0) {
+ Slogf.wtf(TAG, "Error parsing policy size, size is " + size);
+ return;
+ }
+ if (!mAdminPolicySize.contains(admin.getUserId())) {
+ mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+ }
+ mAdminPolicySize.get(admin.getUserId()).put(admin, size);
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f604932..6aa135a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,7 +241,6 @@
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import static android.provider.Telephony.Carriers.INVALID_APN_ID;
import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
-
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -23455,7 +23454,6 @@
public DevicePolicyState getDevicePolicyState() {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
return mInjector.binderWithCleanCallingIdentity(mDevicePolicyEngine::getDevicePolicyState);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
index 9fe3749..7e17ef1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy.flags;
+import static com.android.server.devicepolicy.flags.Flags.devicePolicySizeTrackingEnabled;
import static com.android.server.devicepolicy.flags.Flags.policyEngineMigrationV2Enabled;
import android.os.Binder;
@@ -28,4 +29,10 @@
return policyEngineMigrationV2Enabled();
});
}
+
+ public static boolean isDevicePolicySizeTrackingEnabled() {
+ return Binder.withCleanCallingIdentity(() -> {
+ return devicePolicySizeTrackingEnabled();
+ });
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
index 00702a9..0dde496 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
@@ -5,4 +5,10 @@
namespace: "enterprise"
description: "V2 of the policy engine migrations for Android V"
bug: "289520697"
+}
+flag {
+ name: "device_policy_size_tracking_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track the total policy size and have a max threshold."
+ bug: "281543351"
}
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS
index b2dcd0c..5732844 100644
--- a/services/foldables/devicestateprovider/OWNERS
+++ b/services/foldables/devicestateprovider/OWNERS
@@ -1,6 +1,6 @@
akulian@google.com
-kennethford@google.com
jiamingliu@google.com
kchyn@google.com
+kennethford@google.com
nickchameyev@google.com
nicomazz@google.com
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/TEST_MAPPING b/services/foldables/devicestateprovider/TEST_MAPPING
index cd0d851..47de131 100644
--- a/services/foldables/devicestateprovider/TEST_MAPPING
+++ b/services/foldables/devicestateprovider/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "foldable-services-tests",
+ "name": "foldable-device-state-provider-tests",
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/services/foldables/devicestateprovider/tests/AndroidTest.xml b/services/foldables/devicestateprovider/tests/AndroidTest.xml
index 55e0d9c..f5fdac7 100644
--- a/services/foldables/devicestateprovider/tests/AndroidTest.xml
+++ b/services/foldables/devicestateprovider/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.foldableslib.tests" />
+ <option name="package" value="com.android.foldablesdevicestatelib.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 4a2bf75..5d3eba8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -17,24 +17,22 @@
package com.android.server.pm;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
+
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.AppGlobals;
-import android.content.IIntentReceiver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
-import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -87,18 +85,6 @@
@Test
public void testPackageRemoval() {
class PackageSenderImpl implements PackageSender {
- public void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final int flags, final String targetPkg,
- final IIntentReceiver finishedReceiver, final int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
- @Nullable Bundle bOptions) {
- }
-
- public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootComplete, boolean includeStopped, int appId,
- int[] userIds, int[] instantUserIds, boolean isArchived, int dataLoaderType) {
- }
-
@Override
public void notifyPackageAdded(String packageName, int uid) {
}
@@ -113,9 +99,8 @@
}
}
- PackageSenderImpl sender = new PackageSenderImpl();
PackageSetting setting = null;
- PackageRemovedInfo pri = new PackageRemovedInfo(sender);
+ PackageRemovedInfo pri = new PackageRemovedInfo();
// Initial conditions: nothing there
Assert.assertNull(pri.mRemovedUsers);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
index 2fd6e5f..7a4327c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -27,15 +28,18 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.Display;
+import android.view.DisplayAdjustments;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
@@ -59,6 +63,7 @@
private static final float EPSILON = 0.00001f;
private static final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+ private static final float BRIGHTNESS_MAX = 0.6f;
private Context mContext;
private MockContentResolver mContentResolverSpy;
@@ -66,6 +71,7 @@
private DisplayListener mDisplayListener;
private ContentObserver mContentObserver;
private TestLooper mTestLooper;
+ private BrightnessSynchronizer mSynchronizer;
@Mock private DisplayManager mDisplayManagerMock;
@Captor private ArgumentCaptor<DisplayListener> mDisplayListenerCaptor;
@@ -74,7 +80,17 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+
+ Display display = mock(Display.class);
+ when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
+ BrightnessInfo info = new BrightnessInfo(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ PowerManager.BRIGHTNESS_MIN, BRIGHTNESS_MAX,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, BRIGHTNESS_MAX,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+ when(display.getBrightnessInfo()).thenReturn(info);
+
+ mContext = spy(new ContextWrapper(
+ ApplicationProvider.getApplicationContext().createDisplayContext(display)));
mContentResolverSpy = spy(new MockContentResolver(mContext));
mContentResolverSpy.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolverSpy);
@@ -128,13 +144,12 @@
@Test
public void testSetSameIntValue_nothingUpdated() {
putFloatSetting(0.5f);
- putIntSetting(128);
start();
- putIntSetting(128);
+ putIntSetting(fToI(0.5f));
advanceTime(10);
verify(mDisplayManagerMock, times(0)).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(iToF(128)));
+ eq(Display.DEFAULT_DISPLAY), eq(0.5f));
}
@Test
@@ -154,14 +169,13 @@
// Verify that this update did not get sent to float, because synchronizer
// is still waiting for confirmation of its first value.
verify(mDisplayManagerMock, times(0)).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+ Display.DEFAULT_DISPLAY, iToF(20));
// Send the confirmation of the initial change. This should trigger the new value to
// finally be processed and we can verify that the new value (20) is sent.
putIntSetting(fToI(0.4f));
advanceTime(10);
- verify(mDisplayManagerMock).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+ verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
}
@@ -183,8 +197,7 @@
advanceTime(200);
// Verify that the new value gets sent because the timeout expired.
- verify(mDisplayManagerMock).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+ verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
// Send a confirmation of the initial event, BrightnessSynchronizer should treat this as a
// new event because the timeout had already expired
@@ -196,14 +209,14 @@
// Verify we sent what would have been the confirmation as a new event to displaymanager.
// We do both fToI and iToF because the conversions are not symmetric.
- verify(mDisplayManagerMock).setBrightness(
- eq(Display.DEFAULT_DISPLAY), eq(iToF(fToI(0.4f))));
+ verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY,
+ iToF(fToI(0.4f)));
}
- private BrightnessSynchronizer start() {
- BrightnessSynchronizer bs = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
+ private void start() {
+ mSynchronizer = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
mClock::now);
- bs.startSynchronizing();
+ mSynchronizer.startSynchronizing();
verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(),
isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
mDisplayListener = mDisplayListenerCaptor.getValue();
@@ -211,7 +224,6 @@
verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false),
mContentObserverCaptor.capture(), eq(UserHandle.USER_ALL));
mContentObserver = mContentObserverCaptor.getValue();
- return bs;
}
private int getIntSetting() throws Exception {
@@ -241,11 +253,11 @@
}
private int fToI(float brightness) {
- return BrightnessSynchronizer.brightnessFloatToInt(brightness);
+ return mSynchronizer.brightnessFloatToIntSetting(brightness);
}
private float iToF(int brightness) {
- return BrightnessSynchronizer.brightnessIntToFloat(brightness);
+ return mSynchronizer.brightnessIntSettingToFloat(brightness);
}
private void advanceTime(long timeMs) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index a23539e..306de52 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -71,10 +71,12 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -87,11 +89,13 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
import android.view.ContentRecordingSession;
import android.view.Display;
+import android.view.DisplayAdjustments;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
@@ -99,7 +103,6 @@
import android.view.SurfaceControl;
import android.window.DisplayWindowPolicyController;
-import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -118,11 +121,11 @@
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import com.google.common.truth.Expect;
-
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -198,9 +201,10 @@
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
return new LocalDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ displayAdapterListener, flags, new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
@@ -244,8 +248,14 @@
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new LocalDisplayAdapter(
+ syncRoot,
+ context,
+ handler,
+ displayAdapterListener,
+ flags,
new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
@@ -323,7 +333,11 @@
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
// TODO: b/287945043
- mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ Display display = mock(Display.class);
+ when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
+ when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
+ mContext = spy(new ContextWrapper(
+ ApplicationProvider.getApplicationContext().createDisplayContext(display)));
mResources = Mockito.spy(mContext.getResources());
manageDisplaysPermission(/* granted= */ false);
when(mContext.getResources()).thenReturn(mResources);
@@ -1898,7 +1912,6 @@
@Test
public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() {
- Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
// get the first two internal displays
@@ -2445,6 +2458,86 @@
assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED,
EVENT_DISPLAY_DISCONNECTED);
}
+
+ @Test
+ public void testRegisterDisplayOffloader_whenEnabled_DisplayHasDisplayOffloadSession() {
+ when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ // set up DisplayManager
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ // set up display
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+ initDisplayPowerController(localService);
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+
+ // Register DisplayOffloader.
+ DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+ localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+ assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo(
+ mockDisplayOffloader);
+ }
+
+ @Test
+ public void testRegisterDisplayOffloader_whenDisabled_DisplayHasNoDisplayOffloadSession() {
+ when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(false);
+ // set up DisplayManager
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ // set up display
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+ initDisplayPowerController(localService);
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+
+ // Register DisplayOffloader.
+ DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+ localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+ assertThat(display.getDisplayOffloadSessionLocked()).isNull();
+ }
+
+ private void initDisplayPowerController(DisplayManagerInternal localService) {
+ localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
+ @Override
+ public void onStateChanged() {
+
+ }
+
+ @Override
+ public void onProximityPositive() {
+
+ }
+
+ @Override
+ public void onProximityNegative() {
+
+ }
+
+ @Override
+ public void onDisplayStateChange(boolean allInactive, boolean allOff) {
+
+ }
+
+ @Override
+ public void acquireSuspendBlocker(String id) {
+
+ }
+
+ @Override
+ public void releaseSuspendBlocker(String id) {
+
+ }
+ }, new Handler(Looper.getMainLooper()), mSensorManager);
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index a56b59a..dca69eb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -44,6 +44,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -123,6 +124,9 @@
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
+ private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@Mock
@@ -1409,6 +1413,111 @@
BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int unSupportedTargetState = Display.STATE_ON;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+ // set up.
+ int initState = Display.STATE_OFF;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -1742,9 +1851,9 @@
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
return new BrightnessRangeController(hbmController, modeChangeCallback,
- displayDeviceConfig, mHdrClamper, mFlags);
+ displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
}
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 0572117..edaa1d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -44,6 +44,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -122,6 +123,8 @@
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
+ private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -1364,6 +1367,110 @@
verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int unSupportedTargetState = Display.STATE_ON;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+ // set up.
+ int initState = Display.STATE_OFF;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 6cde5e3..147e8f2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -32,12 +32,15 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -55,6 +58,7 @@
import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -72,6 +76,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -107,8 +112,15 @@
private LightsManager mMockedLightsManager;
@Mock
private LogicalLight mMockedBacklight;
+ @Mock
+ private DisplayManagerFlags mFlags;
+
private Handler mHandler;
+ private DisplayOffloadSession mDisplayOffloadSession;
+
+ private DisplayOffloader mDisplayOffloader;
+
private TestListener mListener = new TestListener();
private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>();
@@ -120,6 +132,8 @@
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final int[] BACKLIGHT_RANGE = { 1, 255 };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
+ private static final List<Integer> mDisplayOffloadSupportedStates
+ = new ArrayList<>(List.of(Display.STATE_DOZE_SUSPEND));
@Before
public void setUp() throws Exception {
@@ -134,7 +148,7 @@
mInjector = new Injector();
when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
- mListener, mInjector);
+ mListener, mFlags, mInjector);
spyOn(mAdapter);
doReturn(mMockedContext).when(mAdapter).getOverlayContext();
@@ -185,6 +199,8 @@
when(mMockedResources.getIntArray(
com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(new int[]{});
+ doReturn(true).when(mFlags).isDisplayOffloadEnabled();
+ initDisplayOffloadSession();
}
@After
@@ -1109,6 +1125,72 @@
assertThat(info.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP).isEqualTo(0);
}
+
+ @Test
+ public void test_displayStateToSupportedState_DisplayOffloadStart()
+ throws InterruptedException {
+ // prepare a display.
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ for (Integer supportedState : mDisplayOffloadSupportedStates) {
+ Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(
+ supportedState, 0, 0, mDisplayOffloadSession);
+ changeStateRunnable.run();
+
+ verify(mDisplayOffloader).startOffload();
+ }
+ }
+
+ @Test
+ public void test_displayStateToDozeFromDozeSuspend_DisplayOffloadStop()
+ throws InterruptedException {
+ // prepare a display.
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ Runnable changeStateToDozeSuspendRunnable = displayDevice.requestDisplayStateLocked(
+ Display.STATE_DOZE_SUSPEND, 0, 0, mDisplayOffloadSession);
+ Runnable changeStateToDozeRunnable = displayDevice.requestDisplayStateLocked(
+ Display.STATE_DOZE, 0, 0, mDisplayOffloadSession);
+ changeStateToDozeSuspendRunnable.run();
+ changeStateToDozeRunnable.run();
+
+ verify(mDisplayOffloader).stopOffload();
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
+
private void setupCutoutAndRoundedCorners() {
String sampleCutout = "M 507,66\n"
+ "a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 37d966d..c3322ec 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -20,6 +20,12 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
import android.os.PowerManager;
import androidx.test.filters.SmallTest;
@@ -31,6 +37,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -40,7 +47,20 @@
@SmallTest
public class HdrClamperTest {
- public static final float FLOAT_TOLERANCE = 0.0001f;
+ private static final float FLOAT_TOLERANCE = 0.0001f;
+ private static final long SEND_TIME_TOLERANCE = 100;
+
+ private static final HdrBrightnessData TEST_HDR_DATA = new HdrBrightnessData(
+ Map.of(500f, 0.6f),
+ /* brightnessIncreaseDebounceMillis= */ 1000,
+ /* brightnessIncreaseDurationMillis= */ 2000,
+ /* brightnessDecreaseDebounceMillis= */ 3000,
+ /* brightnessDecreaseDurationMillis= */4000
+ );
+
+ private static final int WIDTH = 600;
+ private static final int HEIGHT = 800;
+ private static final float MIN_HDR_PERCENT = 0.5f;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -48,17 +68,31 @@
@Mock
private BrightnessClamperController.ClamperChangeListener mMockListener;
+ @Mock
+ private IBinder mMockBinder;
+
+ @Mock
+ private HdrClamper.Injector mMockInjector;
+
+ @Mock
+ private HdrClamper.HdrLayerInfoListener mMockHdrInfoListener;
+
OffsettableClock mClock = new OffsettableClock.Stopped();
private final TestHandler mTestHandler = new TestHandler(null, mClock);
private HdrClamper mHdrClamper;
-
+ private HdrClamper.HdrListener mHdrChangeListener;
@Before
public void setUp() {
- mHdrClamper = new HdrClamper(mMockListener, mTestHandler);
+ when(mMockInjector.getHdrListener(any(), any())).thenReturn(mMockHdrInfoListener);
+ mHdrClamper = new HdrClamper(mMockListener, mTestHandler, mMockInjector);
+ ArgumentCaptor<HdrClamper.HdrListener> listenerCaptor = ArgumentCaptor.forClass(
+ HdrClamper.HdrListener.class);
+ verify(mMockInjector).getHdrListener(listenerCaptor.capture(), eq(mTestHandler));
+ mHdrChangeListener = listenerCaptor.getValue();
configureClamper();
}
@@ -68,20 +102,23 @@
assertFalse(mTestHandler.hasMessagesOrCallbacks());
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
- public void testClamper_AmbientLuxChangesBelowLimit() {
+ public void testClamper_AmbientLuxChangesBelowLimit_MaxDecrease() {
mHdrClamper.onAmbientLuxChange(499);
assertTrue(mTestHandler.hasMessagesOrCallbacks());
TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
- assertEquals(2000, msgInfo.sendTime);
+ assertSendTime(3000, msgInfo.sendTime);
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
- mClock.fastForward(2000);
+ mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 4
}
@Test
@@ -91,33 +128,65 @@
assertFalse(mTestHandler.hasMessagesOrCallbacks());
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() {
mHdrClamper.onAmbientLuxChange(499);
- mClock.fastForward(2000);
+ mClock.fastForward(3000);
mTestHandler.timeAdvance();
mHdrClamper.onAmbientLuxChange(500);
assertTrue(mTestHandler.hasMessagesOrCallbacks());
TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
- assertEquals(3000, msgInfo.sendTime); // 2000 + 1000
+ assertSendTime(4000, msgInfo.sendTime); // 3000 + 1000
mClock.fastForward(1000);
mTestHandler.timeAdvance();
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.2f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 2
+ }
+
+ @Test
+ public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit() {
+ mHdrChangeListener.onHdrVisible(false);
+ mHdrClamper.onAmbientLuxChange(499);
+
+ assertFalse(mTestHandler.hasMessagesOrCallbacks());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit_ThenHdrOn() {
+ mHdrChangeListener.onHdrVisible(false);
+ mHdrClamper.onAmbientLuxChange(499);
+ mHdrChangeListener.onHdrVisible(true);
+
+ assertTrue(mTestHandler.hasMessagesOrCallbacks());
+ TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+ assertSendTime(3000, msgInfo.sendTime);
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+
+ mClock.fastForward(3000);
+ mTestHandler.timeAdvance();
+ assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ }
+
+ // MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis()
+ // (in Handler.sendMessageDelayed) and then by subtracting SystemClock.uptimeMillis()
+ // (in TestHandler.sendMessageAtTime, there might be several milliseconds difference between
+ // SystemClock.uptimeMillis() calls, and subtracted value might be greater than added.
+ private static void assertSendTime(long expectedTime, long sendTime) {
+ assertTrue(expectedTime >= sendTime);
+ assertTrue(expectedTime - SEND_TIME_TOLERANCE < sendTime);
}
private void configureClamper() {
- HdrBrightnessData data = new HdrBrightnessData(
- Map.of(500f, 0.6f),
- /* brightnessIncreaseDebounceMillis= */ 1000,
- /* brightnessIncreaseDurationMillis= */ 1500,
- /* brightnessDecreaseDebounceMillis= */ 2000,
- /* brightnessDecreaseDurationMillis= */2500
- );
- mHdrClamper.resetHdrConfig(data);
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+ mHdrChangeListener.onHdrVisible(true);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
index 880501f..f5c6bb2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
@@ -141,6 +141,32 @@
assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
}
+ @Test
+ public void dozeScreenStateOverrideToDozeSuspend_DozePolicy_updateDisplayStateToDozeSuspend() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ new DisplayManagerInternal.DisplayPowerRequest();
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+ int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+ !DISPLAY_IN_TRANSITION);
+
+ assertEquals(state, Display.STATE_DOZE_SUSPEND);
+ }
+
+ @Test
+ public void dozeScreenStateOverrideToDozeSuspend_OffPolicy_displayRemainOff() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ new DisplayManagerInternal.DisplayPowerRequest();
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+ mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+ int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+ !DISPLAY_IN_TRANSITION);
+
+ assertEquals(state, Display.STATE_OFF);
+ }
+
private void validDisplayState(int policy, int displayState, boolean isEnabled,
boolean isInTransition) {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 52044bf..de8b308 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -117,7 +117,9 @@
Build.VERSION_CODES.CUR_DEVELOPMENT,
Build.VERSION.INCREMENTAL);
mMockSystem.system().validateFinalState();
- mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class));
+ mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class),
+ mock(RemovePackageHelper.class), mock(DeletePackageHelper.class),
+ mock(BroadcastHelper.class));
}
@NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index d6a4d40..931b38d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -34,6 +34,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
@RunWith(JUnit4::class)
class DeletePackageHelperTest {
@@ -79,7 +80,8 @@
whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1, 0, false)
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -97,7 +99,8 @@
whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
UserInfo(userId, "testparent", 0))
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, userId, 0, false)
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -112,7 +115,8 @@
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -133,7 +137,8 @@
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, userId,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -150,7 +155,8 @@
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -164,7 +170,8 @@
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_GRANTED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index 9f1cec3..cf81f0a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -39,7 +39,7 @@
override fun setup() {
super.setup()
distractingPackageHelper = DistractingPackageHelper(
- pms, rule.mocks().injector, broadcastHelper, suspendPackageHelper)
+ pms, broadcastHelper, suspendPackageHelper)
}
@Test
@@ -50,12 +50,11 @@
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+ pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
- val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+ val modifiedPackages = pkgListCaptor.value
+ val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
assertThat(unactionedPackages).isEmpty()
@@ -75,10 +74,8 @@
PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid)
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(
- eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
assertThat(unactionedPackages).isEmpty()
}
@@ -154,11 +151,11 @@
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID),
+ flagsCaptor.capture())
+ val modifiedPackages = pkgListCaptor.value
+ val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_NONE)
}
@@ -170,9 +167,8 @@
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(eq(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
- nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
}
@Test
@@ -189,22 +185,21 @@
arrayOfNulls(0), TEST_USER_ID)
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(eq(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
- nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
}
@Test
fun sendDistractingPackagesChanged() {
- distractingPackageHelper.sendDistractingPackagesChanged(packagesToChange, uidsToChange,
- TEST_USER_ID, PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+ broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+ packagesToChange, uidsToChange, TEST_USER_ID,
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+ pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
- var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ var changedPackages = pkgListCaptor.value
+ var changedUids = uidsCaptor.value
assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(changedUids).asList().containsExactly(
packageSetting1.appId, packageSetting2.appId)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index 5fd270e..eb00164 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -17,7 +17,6 @@
package com.android.server.pm
import android.os.Build
-import android.os.Bundle
import android.os.UserHandle
import android.os.UserManager
import com.android.server.pm.pkg.PackageStateInternal
@@ -68,7 +67,11 @@
lateinit var protectedPackages: ProtectedPackages
@Captor
- lateinit var bundleCaptor: ArgumentCaptor<Bundle>
+ lateinit var pkgListCaptor: ArgumentCaptor<Array<String>>
+ @Captor
+ lateinit var flagsCaptor: ArgumentCaptor<Int>
+ @Captor
+ lateinit var uidsCaptor: ArgumentCaptor<IntArray>
@Rule
@JvmField
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index c5db5db..6c44fd0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -17,12 +17,12 @@
package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.pm.PackageInstaller;
@@ -63,9 +63,7 @@
@Before
public void setup() {
- when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler);
- mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(
- mMockSystem.mocks().getInjector());
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper();
}
@@ -80,7 +78,7 @@
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -93,7 +91,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
- null /* broadcastAllowList */);
+ null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
@@ -101,7 +99,7 @@
mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -114,7 +112,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -138,7 +136,7 @@
// Notify for user 10
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -155,7 +153,7 @@
mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME,
false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */,
new int[]{0} /* userIds */, null /* instantUserIds */,
- null /* broadcastAllowList */);
+ null /* broadcastAllowList */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -183,7 +181,8 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME,
FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0], false /* isArchived */,
- PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */);
+ PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */,
+ mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -207,7 +206,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */,
true /* replacing */, new String[]{FAKE_PACKAGE_NAME},
- new int[]{FAKE_PACKAGE_UID} /* uids */);
+ new int[]{FAKE_PACKAGE_UID} /* uids */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -240,7 +239,7 @@
mPackageMonitorCallbackHelper.onUserRemoved(10);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -256,7 +255,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
@@ -272,7 +271,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -288,7 +287,7 @@
Process.SYSTEM_UID);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 6797576..4240373 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -22,12 +22,11 @@
import android.os.PersistableBundle
import com.android.server.testutils.any
import com.android.server.testutils.eq
-import com.android.server.testutils.nullable
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -44,17 +43,10 @@
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
- nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(),
- nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
- nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(),
- nullable(), nullable())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(failedNames).isEmpty()
}
@@ -146,6 +138,7 @@
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
+ Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, false /* suspended */, null /* appExtras */,
@@ -154,17 +147,13 @@
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
- nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
- nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(),
+ any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+ any(), any(), any())
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(failedNames).isEmpty()
}
@@ -206,7 +195,7 @@
testHandler.flush()
assertThat(failedNames).isEmpty()
- val result = suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ val result = SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!!
assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1)
@@ -222,14 +211,15 @@
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
+ Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
@@ -238,23 +228,18 @@
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
- nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
- nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+ any(), any(), any())
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
}
@@ -319,39 +304,4 @@
assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
}
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendedForUser() {
- suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID)
- testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
-
- var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(changedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendModifiedForUser() {
- suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID)
- testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(
- eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
-
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(modifiedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 82b7540..2f0257a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -39,11 +39,16 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import android.view.WindowManager;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -76,6 +81,9 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
AccessibilityServiceConnection mConnection;
@Mock AccessibilityUserState mMockUserState;
@@ -113,6 +121,8 @@
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
+ when(mMockContext.getSystemService(Context.DISPLAY_SERVICE))
+ .thenReturn(new DisplayManager(mMockContext));
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -168,6 +178,18 @@
assertFalse(mConnection.getServiceInfo().crashed);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void onServiceConnected_addsWindowTokens() {
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void setServiceBinding(ComponentName componentName) {
when(mMockUserState.getBindingServicesLocked())
.thenReturn(new HashSet<>(Arrays.asList(componentName)));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b4558b2..63281b7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -35,6 +35,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -46,6 +47,9 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
@@ -88,6 +92,9 @@
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock private AccessibilityServiceInfo mMockServiceInfo;
@Mock private AccessibilityServiceConnection mMockConnection;
@@ -188,7 +195,7 @@
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection, never()).onAdded();
+ verify(mMockListener, never()).onServiceInfoChangedLocked(any());
}
@Test
@@ -197,13 +204,24 @@
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection).onAdded();
assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
}
@Test
+ // addServiceLocked only calls addWindowTokensForAllDisplays when
+ // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
+ @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void addService_flagDisabled_addsWindowTokens() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection).addWindowTokensForAllDisplays();
+ }
+
+ @Test
public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
// When soft kb show mode is hidden in settings and is auto in state.
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 4ce9ba0..3ee5f61 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -21,8 +21,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +39,10 @@
import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -43,6 +50,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -58,6 +66,9 @@
MessageCapturingHandler mMessageCapturingHandler;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@@ -197,6 +208,24 @@
assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
+ register(0);
+ // registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
+ verify(mMockWindowManagerInternal, never()).addWindowToken(
+ any(), anyInt(), anyInt(), any());
+
+ // Advance UiAutomationManager#UiAutomationService's handler.
+ mMessageCapturingHandler.sendAllMessages();
+
+ // After advancing the handler we expect addWindowToken to have been called
+ // by the UiAutomationService instance.
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void register(int flags) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a0bca3b..24ad976 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -50,7 +50,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.platform.test.annotations.FlakyTest;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.InputDevice;
@@ -60,6 +59,7 @@
import android.view.accessibility.MagnificationAnimationCallback;
import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index dee7780..37a6d22 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -781,7 +781,7 @@
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.onCleanupUser(PRIMARY_USER_ID);
+ mService.onUserStopped(PRIMARY_USER_ID);
assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
index 2c9ba34..e8b7ad7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
@@ -169,7 +169,7 @@
assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
- mService.onCleanupUser(PRIMARY_USER_ID);
+ mService.onUserStopped(PRIMARY_USER_ID);
assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 020afdb..8d7b5cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -141,6 +141,7 @@
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.protobuf.InvalidProtocolBufferException;
import org.json.JSONArray;
@@ -563,7 +564,7 @@
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -647,7 +648,7 @@
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -2620,6 +2621,16 @@
}
@Test
+ public void testOnlyHasDefaultChannel() throws Exception {
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+ UID_N_MR1, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -2644,16 +2655,6 @@
}
@Test
- public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
-
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
- UID_N_MR1, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- }
-
- @Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
@@ -2884,7 +2885,7 @@
UID_N_MR1});
assertEquals(0, mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList().size());
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList().size());
}
@Test
@@ -3022,7 +3023,7 @@
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -3056,14 +3057,15 @@
channel1.setGroup(ncg.getId());
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
UID_N_MR1, false);
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false, true, null)
+ .getList();
channel1.setImportance(IMPORTANCE_LOW);
mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -3089,7 +3091,7 @@
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, false, true).getList();
+ PKG_N_MR1, UID_N_MR1, false, false, true, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -5786,6 +5788,62 @@
assertFalse(isUserSet);
}
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_includeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, filter).getList().get(0);
+ assertEquals(2, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_doNotIncludeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, false, filter).getList().get(0);
+ assertEquals(1, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
private static NotificationChannel cloneChannel(NotificationChannel original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 61c4d06..8db09f9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,6 +46,7 @@
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -98,6 +99,10 @@
* settings values.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6e2c4bd..ef28ffa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,6 +70,7 @@
import android.hardware.input.InputManager;
import android.media.AudioManagerInternal;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -77,7 +78,6 @@
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
@@ -160,8 +160,8 @@
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
- private TestLooper mTestLooper;
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
@@ -184,11 +184,12 @@
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
- mTestLooper = new TestLooper();
- mHandler = new Handler(mTestLooper.getLooper());
+ mHandlerThread = new HandlerThread("fake window manager");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.post(() -> setUp(supportSettingsUpdate));
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
+ waitForIdle();
}
private void setUp(boolean supportSettingsUpdate) {
@@ -300,6 +301,7 @@
}
void tearDown() {
+ mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
@@ -326,7 +328,7 @@
}
void waitForIdle() {
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> { }, 0 /* timeout */);
}
/**
diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp
new file mode 100644
index 0000000..78b0ede
--- /dev/null
+++ b/tests/BinderLeakTest/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "binder_leak_test_aidl",
+ srcs: ["**/*.aidl"],
+ path: "aidl",
+}
+
+java_defaults {
+ name: "BinderTest.defaults",
+ srcs: [
+ "**/*.java",
+ ":binder_leak_test_aidl",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ ],
+}
+
+// Built with target_sdk_version: current
+android_test {
+ name: "BinderLeakTest",
+ defaults: ["BinderTest.defaults"],
+}
+
+// Built with target_sdk_version: 33
+android_test {
+ name: "BinderLeakTest_legacy",
+ defaults: ["BinderTest.defaults"],
+ manifest: "AndroidManifest_legacy.xml",
+}
diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml
new file mode 100644
index 0000000..756def7
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml
new file mode 100644
index 0000000..03d1dfd
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <uses-sdk android:minSdkVersion="33"
+ android:targetSdkVersion="33"
+ android:maxSdkVersion="33" />
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
new file mode 100644
index 0000000..a721959
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
@@ -0,0 +1,5 @@
+package com.android.test.binder;
+
+interface IFoo {
+
+}
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
new file mode 100644
index 0000000..b487f51
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
@@ -0,0 +1,10 @@
+package com.android.test.binder;
+import com.android.test.binder.IFoo;
+
+interface IFooProvider {
+ IFoo createFoo();
+
+ boolean isFooGarbageCollected();
+
+ oneway void killProcess();
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
new file mode 100644
index 0000000..f07317f
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.test.binder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ServiceTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class BinderTest {
+ @Rule
+ public final ServiceTestRule serviceRule = new ServiceTestRule();
+
+ @Test
+ public void testDeathRecipientLeaksOrNot()
+ throws RemoteException, TimeoutException, InterruptedException {
+ Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class);
+ IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent));
+ FooHolder holder = new FooHolder(provider.createFoo());
+
+ // ref will get enqueued right after holder is finalized for gc.
+ ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>();
+ PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue);
+
+ DeathRecorder deathRecorder = new DeathRecorder();
+ holder.registerDeathRecorder(deathRecorder);
+
+ if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ /////////////////////////////////////////////
+ // New behavior
+ //
+ // Reference chain at this moment:
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // In other words, the variable "holder" is the root of the reference chain.
+
+ // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even
+ // Foo.Stub unreachable.
+ holder = null;
+
+ // Ensure that the objects are garbage collected
+ forceGc();
+ assertEquals(ref, refQueue.poll());
+ assertTrue(provider.isFooGarbageCollected());
+
+ // The binder has died, but we don't get notified since the death recipient is GC'ed.
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertFalse(deathRecorder.deathRecorded);
+ } else {
+ /////////////////////////////////////////////
+ // Legacy behavior
+ //
+ // Reference chain at this moment:
+ // JavaDeathRecipient --(JNI strong ref)--> FooHolder
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // So, BOTH JavaDeathRecipient and holder are roots of the reference chain.
+
+ // Even if we set holder to null, it doesn't make other objects unreachable; they are
+ // still reachable via the JNI strong ref.
+ holder = null;
+
+ // Check that objects are not garbage collected
+ forceGc();
+ assertNotEquals(ref, refQueue.poll());
+ assertFalse(provider.isFooGarbageCollected());
+
+ // The legacy behavior is getting notified even when there's no reference
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertTrue(deathRecorder.deathRecorded);
+ }
+ }
+
+ static class FooHolder implements IBinder.DeathRecipient {
+ private IFoo mProxy;
+ private DeathRecorder mDeathRecorder;
+
+ FooHolder(IFoo proxy) throws RemoteException {
+ proxy.asBinder().linkToDeath(this, 0);
+
+ // A strong reference from DeathRecipient(this) to the binder proxy is created here
+ mProxy = proxy;
+ }
+
+ public void registerDeathRecorder(DeathRecorder dr) {
+ mDeathRecorder = dr;
+ }
+
+ @Override
+ public void binderDied() {
+ if (mDeathRecorder != null) {
+ mDeathRecorder.deathRecorded = true;
+ }
+ }
+ }
+
+ static class DeathRecorder {
+ public boolean deathRecorded = false;
+ }
+
+ // Try calling System.gc() until an orphaned object is confirmed to be finalized
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+
+ private static int getSdkVersion() {
+ return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion;
+ }
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
new file mode 100644
index 0000000..c701253
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.test.binder;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+
+public class MyService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new IFooProvider.Stub() {
+ ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>();
+ PhantomReference<IFoo> mRef;
+
+ @Override
+ public IFoo createFoo() throws RemoteException {
+ IFoo binder = new IFoo.Stub() {};
+ mRef = new PhantomReference<>(binder, mRefQueue);
+ return binder;
+ }
+
+ @Override
+ public boolean isFooGarbageCollected() throws RemoteException {
+ forceGc();
+ return mRefQueue.poll() == mRef;
+ }
+
+ @Override
+ public void killProcess() throws RemoteException {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ };
+ }
+
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 1aa4859..0c1d88a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -13,11 +13,11 @@
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -33,11 +33,11 @@
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -56,11 +56,11 @@
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -76,11 +76,11 @@
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -96,11 +96,11 @@
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -119,11 +119,11 @@
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -139,11 +139,11 @@
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -159,11 +159,11 @@
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -179,11 +179,11 @@
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -199,7 +199,7 @@
}
SourceFile: "HostSideTestSuppress.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
@@ -217,9 +217,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -230,11 +230,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -242,11 +242,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkCallerCheck.java"
@@ -267,9 +267,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -280,8 +280,8 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -289,13 +289,13 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -314,14 +314,14 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int remove;
@@ -333,21 +333,21 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -355,17 +355,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -373,17 +373,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public void toBeRemoved(java.lang.String);
@@ -391,17 +391,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 8 1 foo Ljava/lang/String;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestRemove
public int addTwo(int);
@@ -409,20 +409,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -432,10 +432,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -446,9 +446,9 @@
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -458,10 +458,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -472,14 +472,14 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -487,22 +487,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -532,15 +532,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -551,10 +551,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -566,10 +566,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -581,10 +581,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -596,20 +596,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -619,10 +619,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -633,9 +633,9 @@
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -645,10 +645,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -659,8 +659,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -671,9 +671,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -681,7 +681,7 @@
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -702,9 +702,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -715,11 +715,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -734,16 +734,16 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -763,9 +763,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -776,18 +776,18 @@
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: putstatic #x // Field sInitialized:Z
- 4: return
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -803,9 +803,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -816,18 +816,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -841,7 +841,7 @@
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -869,15 +869,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -888,10 +888,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -903,10 +903,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -918,10 +918,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -933,11 +933,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -949,10 +949,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -968,10 +968,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -982,8 +982,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -994,9 +994,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1017,9 +1017,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1034,9 +1034,9 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1051,10 +1051,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1063,9 +1063,9 @@
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1083,9 +1083,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1096,10 +1096,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1110,10 +1110,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: ladd
- 3: lreturn
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,7 +1122,7 @@
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1142,12 +1142,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1159,9 +1159,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_1
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1172,9 +1172,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1200,9 +1200,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1213,9 +1213,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_2
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1226,9 +1226,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1258,12 +1258,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1275,9 +1275,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_3
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1288,9 +1288,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1316,9 +1316,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1329,9 +1329,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_4
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1342,9 +1342,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1374,12 +1374,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1412,15 +1412,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1429,7 +1429,7 @@
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1448,9 +1448,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1461,9 +1461,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: bipush 7
- 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 5: areturn
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1474,9 +1474,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1507,12 +1507,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1523,16 +1523,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1552,10 +1552,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1591,15 +1591,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1610,11 +1610,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1626,10 +1626,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1638,16 +1638,16 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 6e1528a..43ceec4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -12,33 +12,33 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOneStub();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -55,44 +55,44 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_withCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_noCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -109,7 +109,7 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
@@ -117,13 +117,13 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -131,13 +131,13 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addTwo(int);
@@ -145,47 +145,47 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -215,97 +215,97 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOneInner(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public void toBeRemoved(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -326,22 +326,22 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static void onClassLoaded(java.lang.Class<?>);
descriptor: (Ljava/lang/Class;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // (Ljava/lang/Class<*>;)V
static {};
@@ -349,20 +349,20 @@
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -382,35 +382,35 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -426,31 +426,31 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int testException();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -470,61 +470,61 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -540,11 +540,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native int nativeAddTwo(int);
descriptor: (I)I
@@ -555,11 +555,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -570,22 +570,22 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -607,19 +607,19 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -644,22 +644,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -680,22 +680,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
InnerClasses:
@@ -703,12 +703,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -725,20 +725,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -765,22 +765,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
@@ -788,11 +788,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
static {};
@@ -800,11 +800,11 @@
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -818,12 +818,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 5672e9c..faf0a46 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -13,13 +13,13 @@
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -35,13 +35,13 @@
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -60,13 +60,13 @@
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -82,13 +82,13 @@
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -104,13 +104,13 @@
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -129,13 +129,13 @@
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -151,13 +151,13 @@
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -173,13 +173,13 @@
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -195,13 +195,13 @@
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -219,9 +219,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -232,17 +232,17 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=0, args_size=0
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- 2: ldc #x // String getOneKeep
- 4: ldc #x // String ()I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -250,20 +250,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -280,9 +280,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -293,8 +293,8 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -302,20 +302,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -332,14 +332,14 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
private static {};
@@ -347,31 +347,31 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -379,17 +379,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -397,23 +397,23 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
15 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int addTwo(int);
@@ -421,10 +421,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -436,10 +436,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -450,20 +450,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -471,27 +471,27 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -521,15 +521,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -540,10 +540,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -555,10 +555,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -570,10 +570,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -585,10 +585,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -600,10 +600,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -614,8 +614,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -626,9 +626,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -636,12 +636,12 @@
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -662,9 +662,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -675,11 +675,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -694,21 +694,21 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -728,9 +728,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -741,26 +741,26 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: iconst_1
- 8: putstatic #x // Field sInitialized:Z
- 11: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -776,9 +776,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -789,18 +789,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -814,12 +814,12 @@
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -843,25 +843,25 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -872,10 +872,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -887,16 +887,16 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -908,10 +908,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -923,10 +923,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -937,27 +937,27 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -965,9 +965,9 @@
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -983,9 +983,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -996,18 +996,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1018,20 +1018,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1040,14 +1040,14 @@
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1065,15 +1065,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String <init>
- 4: ldc #x // String ()V
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokespecial #x // Method java/lang/Object."<init>":()V
- 19: return
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1084,16 +1084,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeAddTwo
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_0
- 16: iconst_2
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1104,16 +1104,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeLongPlus
- 4: ldc #x // String (JJ)J
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: lload_0
- 16: lload_2
- 17: ladd
- 18: lreturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,10 +1122,10 @@
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1145,12 +1145,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1162,15 +1162,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1181,15 +1181,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1201,7 +1201,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
@@ -1218,9 +1218,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1231,15 +1231,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_2
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1250,15 +1250,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1270,7 +1270,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
@@ -1291,12 +1291,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1308,15 +1308,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_3
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1327,15 +1327,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1347,7 +1347,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
@@ -1364,9 +1364,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1377,15 +1377,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_4
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1396,15 +1396,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1416,7 +1416,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
@@ -1437,12 +1437,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1453,9 +1453,9 @@
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -1480,15 +1480,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1499,12 +1499,12 @@
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
@@ -1521,9 +1521,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1534,15 +1534,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: bipush 7
- 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 20: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1553,15 +1553,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1574,7 +1574,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -1595,12 +1595,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1611,10 +1611,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
@@ -1623,12 +1623,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -1645,10 +1645,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1660,9 +1660,9 @@
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -1689,15 +1689,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1708,11 +1708,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1724,10 +1724,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1736,11 +1736,11 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
InnerClasses:
@@ -1755,12 +1755,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index 93729fb..992665e 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -93,6 +93,7 @@
if (( $simple )) ; then
# For "simple output" mode,
# - Normalize the constant numbers (replace with "#x")
+ # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
# - Remove the constant pool
# - Remove the line number table
# - Some other transient lines
@@ -100,6 +101,7 @@
# `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
# the start and the end lines.
sed -e 's/#[0-9][0-9]*/#x/g' \
+ -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
-e '/^Constant pool:/,/^[^ ]/{//!d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
-e '/SHA-256 checksum/d' \
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index 4a2e37e..2e82beb 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -14,6 +14,7 @@
import argparse
import json
+import functools
import os
import shutil
import subprocess
@@ -28,6 +29,7 @@
PATH_PREFIX = "out/soong/.intermediates"
PATH_SUFFIX = "android_common/lint"
FIX_ZIP = "suggested-fixes.zip"
+MODULE_JAVA_DEPS = "out/soong/module_bp_java_deps.json"
class SoongModule:
@@ -49,11 +51,26 @@
print(f"Found module {partial_path}/{self._name}.")
self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}"
+ def find_java_deps(self, module_java_deps):
+ """Finds the dependencies of a Java module in the loaded module_bp_java_deps.json.
+
+ Returns:
+ A list of module names.
+ """
+ if self._name not in module_java_deps:
+ raise Exception(f"Module {self._name} not found!")
+
+ return module_java_deps[self._name]["dependencies"]
+
@property
def name(self):
return self._name
@property
+ def path(self):
+ return self._path
+
+ @property
def lint_report(self):
return f"{self._path}/lint-report.txt"
@@ -62,52 +79,25 @@
return f"{self._path}/{FIX_ZIP}"
-class SoongLintFix:
+class SoongLintWrapper:
"""
- This class creates a command line tool that will apply lint fixes to the
- platform via the necessary combination of soong and shell commands.
+ This class wraps the necessary calls to Soong and/or shell commands to lint
+ platform modules and apply suggested fixes if desired.
- It breaks up these operations into a few "private" methods that are
- intentionally exposed so experimental code can tweak behavior.
-
- The entry point, `run`, will apply lint fixes using the intermediate
- `suggested-fixes` directory that soong creates during its invocation of
- lint.
-
- Basic usage:
- ```
- from soong_lint_fix import SoongLintFix
-
- opts = SoongLintFixOptions()
- opts.parse_args(sys.argv)
- SoongLintFix(opts).run()
- ```
+ It breaks up these operations into a few methods that are available to
+ sub-classes (see SoongLintFix for an example).
"""
- def __init__(self, opts):
- self._opts = opts
+ def __init__(self, check=None, lint_module=None):
+ self._check = check
+ self._lint_module = lint_module
self._kwargs = None
- self._modules = []
-
- def run(self):
- """
- Run the script
- """
- self._setup()
- self._find_modules()
- self._lint()
-
- if not self._opts.no_fix:
- self._fix()
-
- if self._opts.print:
- self._print()
def _setup(self):
env = os.environ.copy()
- if self._opts.check:
- env["ANDROID_LINT_CHECK"] = self._opts.check
- if self._opts.lint_module:
- env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
+ if self._check:
+ env["ANDROID_LINT_CHECK"] = self._check
+ if self._lint_module:
+ env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._lint_module
self._kwargs = {
"env": env,
@@ -117,7 +107,10 @@
os.chdir(ANDROID_BUILD_TOP)
- print("Refreshing soong modules...")
+ @functools.cached_property
+ def _module_info(self):
+ """Returns the JSON content of module-info.json."""
+ print("Refreshing Soong modules...")
try:
os.mkdir(ANDROID_PRODUCT_OUT)
except OSError:
@@ -125,19 +118,54 @@
subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs)
print("done.")
-
- def _find_modules(self):
with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
- module_info = json.load(f)
+ return json.load(f)
- for module_name in self._opts.modules:
- module = SoongModule(module_name)
- module.find(module_info)
- self._modules.append(module)
+ def _find_module(self, module_name):
+ """Returns a SoongModule from a module name.
- def _lint(self):
+ Ensures that the module is known to Soong.
+ """
+ module = SoongModule(module_name)
+ module.find(self._module_info)
+ return module
+
+ def _find_modules(self, module_names):
+ modules = []
+ for module_name in module_names:
+ modules.append(self._find_module(module_name))
+ return modules
+
+ @functools.cached_property
+ def _module_java_deps(self):
+ """Returns the JSON content of module_bp_java_deps.json."""
+ print("Refreshing Soong Java deps...")
+ subprocess.call(f"{SOONG_UI} --make-mode {MODULE_JAVA_DEPS}", **self._kwargs)
+ print("done.")
+
+ with open(f"{MODULE_JAVA_DEPS}") as f:
+ return json.load(f)
+
+ def _find_module_java_deps(self, module):
+ """Returns a list a dependencies for a module.
+
+ Args:
+ module: A SoongModule.
+
+ Returns:
+ A list of SoongModule.
+ """
+ deps = []
+ dep_names = module.find_java_deps(self._module_java_deps)
+ for dep_name in dep_names:
+ dep = SoongModule(dep_name)
+ dep.find(self._module_info)
+ deps.append(dep)
+ return deps
+
+ def _lint(self, modules):
print("Cleaning up any old lint results...")
- for module in self._modules:
+ for module in modules:
try:
os.remove(f"{module.lint_report}")
os.remove(f"{module.suggested_fixes}")
@@ -145,13 +173,13 @@
pass
print("done.")
- target = " ".join([ module.lint_report for module in self._modules ])
+ target = " ".join([ module.lint_report for module in modules ])
print(f"Generating {target}")
subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs)
print("done.")
- def _fix(self):
- for module in self._modules:
+ def _fix(self, modules):
+ for module in modules:
print(f"Copying suggested fixes for {module.name} to the tree...")
with zipfile.ZipFile(f"{module.suggested_fixes}") as zip:
for name in zip.namelist():
@@ -161,13 +189,40 @@
shutil.copyfileobj(src, dst)
print("done.")
- def _print(self):
- for module in self._modules:
+ def _print(self, modules):
+ for module in modules:
print(f"### lint-report.txt {module.name} ###", end="\n\n")
with open(module.lint_report, "r") as f:
print(f.read())
+class SoongLintFix(SoongLintWrapper):
+ """
+ Basic usage:
+ ```
+ from soong_lint_fix import SoongLintFix
+
+ opts = SoongLintFixOptions()
+ opts.parse_args()
+ SoongLintFix(opts).run()
+ ```
+ """
+ def __init__(self, opts):
+ super().__init__(check=opts.check, lint_module=opts.lint_module)
+ self._opts = opts
+
+ def run(self):
+ self._setup()
+ modules = self._find_modules(self._opts.modules)
+ self._lint(modules)
+
+ if not self._opts.no_fix:
+ self._fix(modules)
+
+ if self._opts.print:
+ self._print(modules)
+
+
class SoongLintFixOptions:
"""Options for SoongLintFix"""
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
index b5c2ffe..a4c00f7 100644
--- a/tools/lint/utils/enforce_permission_counter.py
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -16,57 +16,38 @@
import soong_lint_fix
-# Libraries that constitute system_server.
-# It is non-trivial to keep in sync with services/Android.bp as some
-# module are post-processed (e.g, services.core).
-TARGETS = [
- "services.core.unboosted",
- "services.accessibility",
- "services.appprediction",
- "services.appwidget",
- "services.autofill",
- "services.backup",
- "services.companion",
- "services.contentcapture",
- "services.contentsuggestions",
- "services.coverage",
- "services.devicepolicy",
- "services.midi",
- "services.musicsearch",
- "services.net",
- "services.people",
- "services.print",
- "services.profcollect",
- "services.restrictions",
- "services.searchui",
- "services.smartspace",
- "services.systemcaptions",
- "services.translation",
- "services.texttospeech",
- "services.usage",
- "services.usb",
- "services.voiceinteraction",
- "services.wallpapereffectsgeneration",
- "services.wifi",
-]
+CHECK = "AnnotatedAidlCounter"
+LINT_MODULE = "AndroidUtilsLintChecker"
-
-class EnforcePermissionMigratedCounter:
+class EnforcePermissionMigratedCounter(soong_lint_fix.SoongLintWrapper):
"""Wrapper around lint_fix to count the number of AIDL methods annotated."""
+
+ def __init__(self):
+ super().__init__(check=CHECK, lint_module=LINT_MODULE)
+
def run(self):
- opts = soong_lint_fix.SoongLintFixOptions()
- opts.check = "AnnotatedAidlCounter"
- opts.lint_module = "AndroidUtilsLintChecker"
- opts.no_fix = True
- opts.modules = TARGETS
+ self._setup()
- self.linter = soong_lint_fix.SoongLintFix(opts)
- self.linter.run()
- self.parse_lint_reports()
+ # Analyze the dependencies of the "services" module and the module
+ # "services.core.unboosted".
+ service_module = self._find_module("services")
+ dep_modules = self._find_module_java_deps(service_module) + \
+ [self._find_module("services.core.unboosted")]
- def parse_lint_reports(self):
+ # Skip dependencies that are not services. Skip the "services.core"
+ # module which is analyzed via "services.core.unboosted".
+ modules = []
+ for module in dep_modules:
+ if "frameworks/base/services" not in module.path:
+ continue
+ if module.name == "services.core":
+ continue
+ modules.append(module)
+
+ self._lint(modules)
+
counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
- for module in self.linter._modules:
+ for module in modules:
with open(module.lint_report, "r") as f:
content = f.read()
keys = dict(re.findall(r'(\w+)=(\d+)', content))