Merge "Use aconfig for Shade Migration flags" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 44f3d70..52200bf 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -180,6 +180,18 @@
srcs: ["core/java/android/nfc/*.aconfig"],
}
+cc_aconfig_library {
+ name: "android_nfc_flags_aconfig_c_lib",
+ vendor_available: true,
+ aconfig_declarations: "android.nfc.flags-aconfig",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ "nfc_nci.st21nfc.default",
+ ],
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 895ef98..a402c576 100644
--- a/Android.bp
+++ b/Android.bp
@@ -249,6 +249,7 @@
"android.se.omapi-V1-java",
"android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
+ "ImmutabilityAnnotation",
"com.android.sysprop.init",
"com.android.sysprop.localization",
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index 29a8dfa..a4174ee 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,13 +1,3 @@
-// b/305195721
-android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:7428: lint: Unresolved link/see tag "android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101]
-android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101]
-
// b/303477132
android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f4a34b..75ac8fb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5316,21 +5316,23 @@
method public android.net.Uri getConditionId();
method @Nullable public android.content.ComponentName getConfigurationActivity();
method public long getCreationTime();
+ method @FlaggedApi("android.app.modes_api") @Nullable public android.service.notification.ZenDeviceEffects getDeviceEffects();
method @FlaggedApi("android.app.modes_api") @DrawableRes public int getIconResId();
method public int getInterruptionFilter();
method public String getName();
method public android.content.ComponentName getOwner();
method @FlaggedApi("android.app.modes_api") @Nullable public String getTriggerDescription();
method @FlaggedApi("android.app.modes_api") public int getType();
- method public android.service.notification.ZenPolicy getZenPolicy();
+ method @Nullable public android.service.notification.ZenPolicy getZenPolicy();
method public boolean isEnabled();
method @FlaggedApi("android.app.modes_api") public boolean isManualInvocationAllowed();
method public void setConditionId(android.net.Uri);
method public void setConfigurationActivity(@Nullable android.content.ComponentName);
+ method @FlaggedApi("android.app.modes_api") public void setDeviceEffects(@Nullable android.service.notification.ZenDeviceEffects);
method public void setEnabled(boolean);
method public void setInterruptionFilter(int);
method public void setName(String);
- method public void setZenPolicy(android.service.notification.ZenPolicy);
+ method public void setZenPolicy(@Nullable android.service.notification.ZenPolicy);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
field @FlaggedApi("android.app.modes_api") public static final int TYPE_BEDTIME = 3; // 0x3
@@ -5350,6 +5352,7 @@
method @NonNull public android.app.AutomaticZenRule build();
method @NonNull public android.app.AutomaticZenRule.Builder setConditionId(@NonNull android.net.Uri);
method @NonNull public android.app.AutomaticZenRule.Builder setConfigurationActivity(@Nullable android.content.ComponentName);
+ method @NonNull public android.app.AutomaticZenRule.Builder setDeviceEffects(@Nullable android.service.notification.ZenDeviceEffects);
method @NonNull public android.app.AutomaticZenRule.Builder setEnabled(boolean);
method @NonNull public android.app.AutomaticZenRule.Builder setIconResId(@DrawableRes int);
method @NonNull public android.app.AutomaticZenRule.Builder setInterruptionFilter(int);
@@ -7861,6 +7864,7 @@
field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
+ field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
}
@@ -14699,31 +14703,31 @@
}
@FlaggedApi("android.database.sqlite.sqlite_apis_35") public final class SQLiteRawStatement implements java.io.Closeable {
- method public void bindBlob(int, @NonNull byte[]) throws android.database.sqlite.SQLiteException;
- method public void bindBlob(int, @NonNull byte[], int, int) throws android.database.sqlite.SQLiteException;
- method public void bindDouble(int, double) throws android.database.sqlite.SQLiteException;
- method public void bindInt(int, int) throws android.database.sqlite.SQLiteException;
- method public void bindLong(int, long) throws android.database.sqlite.SQLiteException;
- method public void bindNull(int) throws android.database.sqlite.SQLiteException;
- method public void bindText(int, @NonNull String) throws android.database.sqlite.SQLiteException;
+ method public void bindBlob(int, @NonNull byte[]);
+ method public void bindBlob(int, @NonNull byte[], int, int);
+ method public void bindDouble(int, double);
+ method public void bindInt(int, int);
+ method public void bindLong(int, long);
+ method public void bindNull(int);
+ method public void bindText(int, @NonNull String);
method public void clearBindings();
method public void close();
- method @Nullable public byte[] getColumnBlob(int) throws android.database.sqlite.SQLiteException;
- method public double getColumnDouble(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnInt(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnLength(int) throws android.database.sqlite.SQLiteException;
- method public long getColumnLong(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnName(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnText(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnType(int) throws android.database.sqlite.SQLiteException;
+ method @Nullable public byte[] getColumnBlob(int);
+ method public double getColumnDouble(int);
+ method public int getColumnInt(int);
+ method public int getColumnLength(int);
+ method public long getColumnLong(int);
+ method @NonNull public String getColumnName(int);
+ method @NonNull public String getColumnText(int);
+ method public int getColumnType(int);
method public int getParameterCount();
method public int getParameterIndex(@NonNull String);
method @Nullable public String getParameterName(int);
method public int getResultColumnCount();
method public boolean isOpen();
- method public int readColumnBlob(int, @NonNull byte[], int, int, int) throws android.database.sqlite.SQLiteException;
+ method public int readColumnBlob(int, @NonNull byte[], int, int, int);
method public void reset();
- method public boolean step() throws android.database.sqlite.SQLiteException;
+ method public boolean step();
field public static final int SQLITE_DATA_TYPE_BLOB = 4; // 0x4
field public static final int SQLITE_DATA_TYPE_FLOAT = 2; // 0x2
field public static final int SQLITE_DATA_TYPE_INTEGER = 1; // 0x1
@@ -22804,11 +22808,11 @@
method public void clearOnSessionLostStateListener();
method public void close();
method public void closeSession(@NonNull byte[]);
- method @android.media.MediaDrm.HdcpLevel public int getConnectedHdcpLevel();
+ method public int getConnectedHdcpLevel();
method public android.media.MediaDrm.CryptoSession getCryptoSession(@NonNull byte[], @NonNull String, @NonNull String);
method @NonNull public android.media.MediaDrm.KeyRequest getKeyRequest(@NonNull byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.HashMap<java.lang.String,java.lang.String>) throws android.media.NotProvisionedException;
method @NonNull public java.util.List<android.media.MediaDrm.LogMessage> getLogMessages();
- method @android.media.MediaDrm.HdcpLevel public int getMaxHdcpLevel();
+ method public int getMaxHdcpLevel();
method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
method public android.os.PersistableBundle getMetrics();
@@ -22822,13 +22826,13 @@
method @Deprecated @NonNull public byte[] getSecureStop(@NonNull byte[]);
method @Deprecated @NonNull public java.util.List<byte[]> getSecureStopIds();
method @Deprecated @NonNull public java.util.List<byte[]> getSecureStops();
- method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
+ method public int getSecurityLevel(@NonNull byte[]);
method @NonNull public static java.util.List<java.util.UUID> getSupportedCryptoSchemes();
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
- method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
+ method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, int);
method @NonNull public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
- method @NonNull public byte[] openSession(@android.media.MediaDrm.SecurityLevel int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
+ method @NonNull public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method @Nullable public byte[] provideKeyResponse(@NonNull byte[], @NonNull byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
method public void provideProvisionResponse(@NonNull byte[]) throws android.media.DeniedByServerException;
method @NonNull public java.util.HashMap<java.lang.String,java.lang.String> queryKeyStatus(@NonNull byte[]);
@@ -22840,7 +22844,7 @@
method public void removeOfflineLicense(@NonNull byte[]);
method @Deprecated public void removeSecureStop(@NonNull byte[]);
method public boolean requiresSecureDecoder(@NonNull String);
- method public boolean requiresSecureDecoder(@NonNull String, @android.media.MediaDrm.SecurityLevel int);
+ method public boolean requiresSecureDecoder(@NonNull String, int);
method public void restoreKeys(@NonNull byte[], @NonNull byte[]);
method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener);
method public void setOnEventListener(@Nullable android.media.MediaDrm.OnEventListener, @Nullable android.os.Handler);
@@ -22929,9 +22933,6 @@
field public static final int ERROR_ZERO_SUBSAMPLES = 33; // 0x21
}
- @Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel {
- }
-
public static final class MediaDrm.KeyRequest {
method @NonNull public byte[] getData();
method @NonNull public String getDefaultUrl();
@@ -23033,9 +23034,6 @@
method @NonNull public String getDefaultUrl();
}
- @Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
- }
-
public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
@@ -28698,14 +28696,17 @@
}
public final class NfcAdapter {
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction();
method public void disableForegroundDispatch(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction();
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
@@ -28813,6 +28814,7 @@
method public boolean removeAidsForService(android.content.ComponentName, String);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
@@ -28832,9 +28834,20 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
@@ -37273,6 +37286,7 @@
}
public static final class Telephony.Carriers implements android.provider.BaseColumns {
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String ALWAYS_ON = "always_on";
field public static final String APN = "apn";
field public static final String AUTH_TYPE = "authtype";
field @Deprecated public static final String BEARER = "bearer";
@@ -37286,6 +37300,8 @@
field public static final String MMSPORT = "mmsport";
field public static final String MMSPROXY = "mmsproxy";
field @Deprecated public static final String MNC = "mnc";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String MTU_V4 = "mtu_v4";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String MTU_V6 = "mtu_v6";
field @Deprecated public static final String MVNO_MATCH_DATA = "mvno_match_data";
field @Deprecated public static final String MVNO_TYPE = "mvno_type";
field public static final String NAME = "name";
@@ -37301,6 +37317,8 @@
field public static final String SUBSCRIPTION_ID = "sub_id";
field public static final String TYPE = "type";
field public static final String USER = "user";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String USER_EDITABLE = "user_editable";
+ field @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public static final String USER_VISIBLE = "user_visible";
}
public static final class Telephony.Mms implements android.provider.Telephony.BaseMmsColumns {
@@ -40697,6 +40715,26 @@
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
}
+ @FlaggedApi("android.app.modes_api") public final class ZenDeviceEffects implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean shouldDimWallpaper();
+ method public boolean shouldDisplayGrayscale();
+ method public boolean shouldSuppressAmbientDisplay();
+ method public boolean shouldUseNightMode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.ZenDeviceEffects> CREATOR;
+ }
+
+ @FlaggedApi("android.app.modes_api") public static final class ZenDeviceEffects.Builder {
+ ctor public ZenDeviceEffects.Builder();
+ ctor public ZenDeviceEffects.Builder(@NonNull android.service.notification.ZenDeviceEffects);
+ method @NonNull public android.service.notification.ZenDeviceEffects build();
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldDimWallpaper(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldDisplayGrayscale(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldSuppressAmbientDisplay(boolean);
+ method @NonNull public android.service.notification.ZenDeviceEffects.Builder setShouldUseNightMode(boolean);
+ }
+
public final class ZenPolicy implements android.os.Parcelable {
method public int describeContents();
method public int getPriorityCallSenders();
@@ -43651,6 +43689,7 @@
field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array";
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_gcm_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
field public static final String KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT = "iwlan.epdg_address_ip_type_preference_int";
@@ -43666,12 +43705,15 @@
field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int";
field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_gcm_key_size_int_array";
field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
field public static final String KEY_PREFIX = "iwlan.";
field public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY = "iwlan.retransmit_timer_sec_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_aead_algorithms_int_array";
field public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_encryption_algorithms_int_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.enable_aead_algorithms") public static final String KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_aead_algorithms_int_array";
field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
@@ -45817,6 +45859,7 @@
method public int getProxyPort();
method public int getRoamingProtocol();
method public String getUser();
+ method @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") public boolean isAlwaysOn();
method public boolean isEnabled();
method public boolean isPersistent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -45856,6 +45899,7 @@
public static class ApnSetting.Builder {
ctor public ApnSetting.Builder();
method public android.telephony.data.ApnSetting build();
+ method @FlaggedApi("com.android.internal.telephony.flags.apn_setting_field_support_flag") @NonNull public android.telephony.data.ApnSetting.Builder setAlwaysOn(boolean);
method @NonNull public android.telephony.data.ApnSetting.Builder setApnName(@Nullable String);
method @NonNull public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
method @NonNull public android.telephony.data.ApnSetting.Builder setAuthType(int);
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1308b1f..449249e 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,40 +1,4 @@
// Baseline format: 1.0
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[]):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[], int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindDouble(int, double):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindInt(int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindLong(int, long):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindNull(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindText(int, String):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnBlob(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnDouble(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnInt(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLength(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLong(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnName(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnText(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnType(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#readColumnBlob(int, byte[], int, int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#step():
- Methods must not throw unchecked exceptions
-
-
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 5a4be65..989bb77 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -224,6 +224,12 @@
ctor public AudioFormat();
}
+ @Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel {
+ }
+
+ @Deprecated @IntDef({android.media.MediaDrm.SECURITY_LEVEL_UNKNOWN, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_SW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_CRYPTO, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_DECODE, android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.SecurityLevel {
+ }
+
}
package android.media.tv {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index de07fdde6..ee1659b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3185,6 +3185,7 @@
field public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2; // 0x2
field public static final int LAUNCH_FAILURE_PENDING_INTENT_CANCELED = 1; // 0x1
field public static final int LAUNCH_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") public static final String PERSISTENT_DEVICE_ID_DEFAULT = "default:0";
}
public static interface VirtualDeviceManager.ActivityListener {
@@ -4762,7 +4763,7 @@
method public void onChange(@NonNull String);
}
- @IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface HdmiControlManager.ControlCallbackResult {
+ @IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HdmiControlManager.ControlCallbackResult {
}
public static interface HdmiControlManager.HotplugEventListener {
@@ -11234,15 +11235,11 @@
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field @Deprecated public static final String MTU = "mtu";
- field public static final String MTU_V4 = "mtu_v4";
- field public static final String MTU_V6 = "mtu_v6";
field public static final int NO_APN_SET_ID = 0; // 0x0
field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
field public static final int UNEDITED = 0; // 0x0
field public static final int USER_DELETED = 2; // 0x2
- field public static final String USER_EDITABLE = "user_editable";
field public static final int USER_EDITED = 1; // 0x1
- field public static final String USER_VISIBLE = "user_visible";
field public static final String WAIT_TIME_RETRY = "wait_time";
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8b20720..75797ed 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -541,7 +541,6 @@
field public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
field public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
field public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
- field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
}
public class DevicePolicyManager {
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3f9cc65..8ad6ea2 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -632,6 +632,7 @@
InputDevice.SOURCE_JOYSTICK,
InputDevice.SOURCE_SENSOR
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface MotionEventSources {}
/**
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index af00f31..6ec956ee 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -24,6 +24,8 @@
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityInteractionClient;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
@@ -92,6 +94,7 @@
STATE_DRAGGING,
STATE_DELEGATING
})
+ @Retention(RetentionPolicy.SOURCE)
private @interface State {}
// The maximum number of pointers that can be touching the screen at once. (See MAX_POINTER_ID
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index be433d2..ed18d81 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -23,7 +23,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import static android.os.Process.myUid;
+
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+
import static java.lang.Character.MIN_VALUE;
import android.annotation.AnimRes;
@@ -1000,6 +1002,7 @@
FULLSCREEN_MODE_REQUEST_EXIT,
FULLSCREEN_MODE_REQUEST_ENTER
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface FullscreenModeRequest {}
/** Request type of {@link #requestFullscreenMode(int, OutcomeReceiver)}, to request exiting the
@@ -1016,6 +1019,7 @@
OVERRIDE_TRANSITION_OPEN,
OVERRIDE_TRANSITION_CLOSE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface OverrideTransition {}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8b4ebae..854e121 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4998,6 +4998,7 @@
STOP_USER_ON_SWITCH_TRUE,
STOP_USER_ON_SWITCH_FALSE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface StopUserOnSwitch {}
/**
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 919e084..a7b29aa 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -22,12 +22,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.NotificationManager.InterruptionFilter;
-import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import android.view.WindowInsetsController;
@@ -111,6 +111,7 @@
private ComponentName configurationActivity;
private long creationTime;
private ZenPolicy mZenPolicy;
+ private ZenDeviceEffects mDeviceEffects;
private boolean mModified = false;
private String mPkg;
private int mType = TYPE_UNKNOWN;
@@ -190,6 +191,7 @@
/**
* @hide
*/
+ // TODO: b/310620812 - Remove when the flag is inlined (all system callers should use Builder).
public AutomaticZenRule(String name, ComponentName owner, ComponentName configurationActivity,
Uri conditionId, ZenPolicy policy, int interruptionFilter, boolean enabled,
long creationTime) {
@@ -209,10 +211,11 @@
configurationActivity = getTrimmedComponentName(
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
- mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
+ mZenPolicy = source.readParcelable(null, ZenPolicy.class);
mModified = source.readInt() == ENABLED;
mPkg = source.readString();
if (Flags.modesApi()) {
+ mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
@@ -274,10 +277,18 @@
/**
* Gets the zen policy.
*/
+ @Nullable
public ZenPolicy getZenPolicy() {
return mZenPolicy == null ? null : this.mZenPolicy.copy();
}
+ /** Gets the {@link ZenDeviceEffects} of this rule. */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public ZenDeviceEffects getDeviceEffects() {
+ return mDeviceEffects;
+ }
+
/**
* Returns the time this rule was created, represented as milliseconds since the epoch.
*/
@@ -325,11 +336,21 @@
/**
* Sets the zen policy.
*/
- public void setZenPolicy(ZenPolicy zenPolicy) {
+ public void setZenPolicy(@Nullable ZenPolicy zenPolicy) {
this.mZenPolicy = (zenPolicy == null ? null : zenPolicy.copy());
}
/**
+ * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes to
+ * the device behavior that should apply while the rule is active, but are not directly related
+ * to suppressing notifications (for example: disabling always-on display).
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
+ mDeviceEffects = deviceEffects;
+ }
+
+ /**
* Sets the configuration activity - an activity that handles
* {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
* about this rule and/or allows them to configure it. This is required to be non-null for rules
@@ -451,6 +472,7 @@
dest.writeInt(mModified ? ENABLED : DISABLED);
dest.writeString(mPkg);
if (Flags.modesApi()) {
+ dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
dest.writeString(mTriggerDescription);
@@ -472,7 +494,8 @@
.append(",mZenPolicy=").append(mZenPolicy);
if (Flags.modesApi()) {
- sb.append(",allowManualInvocation=").append(mAllowManualInvocation)
+ sb.append(",deviceEffects=").append(mDeviceEffects)
+ .append(",allowManualInvocation=").append(mAllowManualInvocation)
.append(",iconResId=").append(mIconResId)
.append(",triggerDescription=").append(mTriggerDescription)
.append(",type=").append(mType);
@@ -498,6 +521,7 @@
&& other.creationTime == creationTime;
if (Flags.modesApi()) {
return finalEquals
+ && Objects.equals(other.mDeviceEffects, mDeviceEffects)
&& other.mAllowManualInvocation == mAllowManualInvocation
&& other.mIconResId == mIconResId
&& Objects.equals(other.mTriggerDescription, mTriggerDescription)
@@ -510,8 +534,8 @@
public int hashCode() {
if (Flags.modesApi()) {
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
- configurationActivity, mZenPolicy, mModified, creationTime, mPkg,
- mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
+ configurationActivity, mZenPolicy, mDeviceEffects, mModified, creationTime,
+ mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
}
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
@@ -573,6 +597,7 @@
private boolean mEnabled;
private ComponentName mConfigurationActivity = null;
private ZenPolicy mPolicy = null;
+ private ZenDeviceEffects mDeviceEffects = null;
private int mType;
private String mDescription;
private int mIconResId;
@@ -588,6 +613,7 @@
mEnabled = rule.isEnabled();
mConfigurationActivity = rule.getConfigurationActivity();
mPolicy = rule.getZenPolicy();
+ mDeviceEffects = rule.getDeviceEffects();
mType = rule.getType();
mDescription = rule.getTriggerDescription();
mIconResId = rule.getIconResId();
@@ -639,6 +665,17 @@
}
/**
+ * Sets the {@link ZenDeviceEffects} associated to this rule. Device effects specify changes
+ * to the device behavior that should apply while the rule is active, but are not directly
+ * related to suppressing notifications (for example: disabling always-on display).
+ */
+ @NonNull
+ public Builder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
+ mDeviceEffects = deviceEffects;
+ return this;
+ }
+
+ /**
* Sets the type of the rule
*/
public @NonNull Builder setType(@Type int type) {
@@ -687,6 +724,7 @@
public @NonNull AutomaticZenRule build() {
AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
mConditionId, mPolicy, mInterruptionFilter, mEnabled);
+ rule.mDeviceEffects = mDeviceEffects;
rule.creationTime = mCreationTime;
rule.mType = mType;
rule.mTriggerDescription = mDescription;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ec5effd..9438571 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -214,6 +214,8 @@
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
AutomaticZenRule getAutomaticZenRule(String id);
+ Map<String, AutomaticZenRule> getAutomaticZenRules();
+ // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
List<ZenModeConfig.ZenRule> getZenRules();
String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 545ba8e..6aad168 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -64,6 +64,8 @@
import com.android.internal.widget.PasswordValidationError;
import com.android.internal.widget.VerifyCredentialResponse;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
@@ -235,6 +237,7 @@
PIN,
PATTERN
})
+ @Retention(RetentionPolicy.SOURCE)
@interface LockTypes {}
private final IKeyguardLockedStateListener mIKeyguardLockedStateListener =
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2d80b1f..337e3f1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -924,6 +924,7 @@
VISIBILITY_SECRET,
NotificationManager.VISIBILITY_NO_OVERRIDE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationVisibilityOverride{};
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index ffb79b3..51c937d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1256,17 +1256,21 @@
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
INotificationManager service = getService();
try {
- List<ZenModeConfig.ZenRule> rules = service.getZenRules();
- Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
- for (ZenModeConfig.ZenRule rule : rules) {
- AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
- rule.configurationActivity, rule.conditionId, rule.zenPolicy,
- zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime);
- azr.setPackageName(rule.pkg);
- ruleMap.put(rule.id, azr);
+ if (Flags.modesApi()) {
+ return service.getAutomaticZenRules();
+ } else {
+ List<ZenModeConfig.ZenRule> rules = service.getZenRules();
+ Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
+ for (ZenModeConfig.ZenRule rule : rules) {
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity, rule.conditionId, rule.zenPolicy,
+ zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+ rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ ruleMap.put(rule.id, azr);
+ }
+ return ruleMap;
}
- return ruleMap;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 019a1a8..4621634 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -42,6 +42,8 @@
import android.view.WindowManager;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -120,6 +122,7 @@
WINDOWING_MODE_PINNED,
WINDOWING_MODE_FREEFORM,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface WindowingMode {}
/** The current activity type of the configuration. */
@@ -147,6 +150,7 @@
ACTIVITY_TYPE_ASSISTANT,
ACTIVITY_TYPE_DREAM,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ActivityType {}
/** The current always on top status of the configuration. */
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index e4ee959..14462b8 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -47,6 +47,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -175,6 +177,7 @@
public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
@IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED})
+ @Retention(RetentionPolicy.SOURCE)
private @interface HeadlessDeviceOwnerMode {}
/** @hide */
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 84b1ca5..b0bec78 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -164,11 +164,8 @@
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
- @TestApi
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3df11f6..1d036a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3998,8 +3998,7 @@
/**
* An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which
- * resource IDs (see {@link DevicePolicyResources.Drawables} and
- * {@link DevicePolicyResources.Strings}) have been updated.
+ * resource IDs (i.e. strings and drawables) have been updated.
*/
public static final String EXTRA_RESOURCE_IDS =
"android.app.extra.RESOURCE_IDS";
@@ -8370,9 +8369,7 @@
* Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
* successfully set or not. This callback will contain:
* <ul>
- * <li> The policy identifier returned from
- * {@link DevicePolicyIdentifiers#getIdentifierForUserRestriction(String)} with user restriction
- * {@link UserManager#DISALLOW_CAMERA}
+ * <li> The policy identifier: userRestriction_no_camera
* <li> The {@link TargetUser} that this policy relates to
* <li> The {@link PolicyUpdateResult}, which will be
* {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
@@ -17113,19 +17110,19 @@
* Returns {@code true} if this device is marked as a financed device.
*
* <p>A financed device can be entered into lock task mode (see {@link #setLockTaskPackages})
- * by the holder of the role {@link android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK}.
+ * by the holder of the role {@code android.app.role.RoleManager#ROLE_FINANCED_DEVICE_KIOSK}.
* If this occurs, Device Owners and Profile Owners that have set lock task packages or
* features, or that attempt to set lock task packages or features, will receive a callback
* indicating that it could not be set. See {@link PolicyUpdateReceiver#onPolicyChanged} and
* {@link PolicyUpdateReceiver#onPolicySetResult}.
*
* <p>To be informed of changes to this status you can subscribe to the broadcast
- * {@link ACTION_DEVICE_FINANCING_STATE_CHANGED}.
+ * {@link #ACTION_DEVICE_FINANCING_STATE_CHANGED}.
*
* @throws SecurityException if the caller is not a device owner, profile owner of an
* organization-owned managed profile, profile owner on the primary user or holder of one of the
- * following roles: {@link android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT},
- * android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION.
+ * following roles: {@code android.app.role.RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT},
+ * {@code android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION}.
*/
public boolean isDeviceFinanced() {
throwIfParentInstance("isDeviceFinanced");
diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java
index 2cc189f..7a71231 100644
--- a/core/java/android/app/admin/DevicePolicyResourcesManager.java
+++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java
@@ -452,7 +452,7 @@
/**
* Returns the appropriate updated string for the {@code stringId} (see
- * {@link DevicePolicyResources.Strings}) if one was set using
+ * {@code DevicePolicyResources.Strings}) if one was set using
* {@code setStrings}, otherwise returns the string from {@code defaultStringLoader}.
*
* <p>Also returns the string from {@code defaultStringLoader} if {@code stringId} is
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index a6595fe..b5c66ff 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -86,7 +86,9 @@
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
EVENT_VENDOR_WEARABLE_START,
- }) public @interface EventCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventCode {}
/** The integer indicating an unknown level. */
public static final int LEVEL_UNKNOWN = 0;
@@ -114,7 +116,9 @@
LEVEL_MEDIUM,
LEVEL_MEDIUM_HIGH,
LEVEL_HIGH
- }) public @interface LevelValue {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LevelValue {}
@EventCode private final int mEventType;
private static int defaultEventType() {
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
index bf383f1..159481f 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -32,6 +32,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -106,7 +108,9 @@
STATUS_SERVICE_UNAVAILABLE,
STATUS_MICROPHONE_DISABLED,
STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusCode {}
/**
* Allows clients to retrieve the list of {@link AmbientContextEvent}s from the intent.
diff --git a/core/java/android/app/cloudsearch/SearchResponse.java b/core/java/android/app/cloudsearch/SearchResponse.java
index c86142e..dab1657 100644
--- a/core/java/android/app/cloudsearch/SearchResponse.java
+++ b/core/java/android/app/cloudsearch/SearchResponse.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -37,6 +39,7 @@
SEARCH_STATUS_OK,
SEARCH_STATUS_TIME_OUT,
SEARCH_STATUS_NO_INTERNET})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SearchStatusCode {
}
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index dd332c8..bc8fac5 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -71,17 +71,13 @@
@NonNull
public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
@NonNull Configuration config) {
- if (config == null) {
- throw new IllegalArgumentException("Config must not be null.");
- }
-
ActivityConfigurationChangeItem instance =
ObjectPool.obtain(ActivityConfigurationChangeItem.class);
if (instance == null) {
instance = new ActivityConfigurationChangeItem();
}
instance.setActivityToken(activityToken);
- instance.mConfiguration = config;
+ instance.mConfiguration = new Configuration(config);
return instance;
}
@@ -89,7 +85,7 @@
@Override
public void recycle() {
super.recycle();
- mConfiguration = Configuration.EMPTY;
+ mConfiguration = null;
ObjectPool.recycle(this);
}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index a5dd115..3ce094e 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -32,6 +32,7 @@
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -51,7 +52,7 @@
/**
* A record that was properly configured for relaunch. Execution will be cancelled if not
- * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+ * initialized after {@link #preExecute(ClientTransactionHandler)}.
*/
private ActivityClientRecord mActivityClientRecord;
@@ -99,10 +100,11 @@
instance = new ActivityRelaunchItem();
}
instance.setActivityToken(activityToken);
- instance.mPendingResults = pendingResults;
- instance.mPendingNewIntents = pendingNewIntents;
+ instance.mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null;
+ instance.mPendingNewIntents =
+ pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null;
instance.mConfigChanges = configChanges;
- instance.mConfig = config;
+ instance.mConfig = new MergedConfiguration(config);
instance.mPreserveWindow = preserveWindow;
return instance;
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 24fced4..51a09fb 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -35,6 +35,7 @@
import android.os.Parcelable;
import android.os.Trace;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -82,7 +83,7 @@
instance = new ActivityResultItem();
}
instance.setActivityToken(activityToken);
- instance.mResultInfoList = resultInfoList;
+ instance.mResultInfoList = new ArrayList<>(resultInfoList);
return instance;
}
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 2a65b35..b4ff476f 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -20,6 +20,8 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -93,7 +95,7 @@
}
void setActivityToken(@NonNull IBinder activityToken) {
- mActivityToken = activityToken;
+ mActivityToken = requireNonNull(activityToken);
}
// To be overridden
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 9c0cd39..7c34cde 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -54,12 +54,14 @@
/** A list of individual callbacks to a client. */
@UnsupportedAppUsage
+ @Nullable
private List<ClientTransactionItem> mActivityCallbacks;
/**
* Final lifecycle state in which the client activity should be after the transaction is
* executed.
*/
+ @Nullable
private ActivityLifecycleItem mLifecycleStateRequest;
/** Target client. */
@@ -123,6 +125,7 @@
@VisibleForTesting(visibility = PACKAGE)
@UnsupportedAppUsage
@Deprecated
+ @Nullable
public ActivityLifecycleItem getLifecycleStateRequest() {
return mLifecycleStateRequest;
}
@@ -207,7 +210,7 @@
for (int i = 0; i < size; i++) {
mActivityCallbacks.get(i).recycle();
}
- mActivityCallbacks.clear();
+ mActivityCallbacks = null;
}
if (mLifecycleStateRequest != null) {
mLifecycleStateRequest.recycle();
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 96961ace..0e327a7 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -64,7 +64,7 @@
if (instance == null) {
instance = new ConfigurationChangeItem();
}
- instance.mConfiguration = config;
+ instance.mConfiguration = new Configuration(config);
instance.mDeviceId = deviceId;
return instance;
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index a64c744..d2ef65a 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -45,6 +45,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -135,10 +136,16 @@
if (instance == null) {
instance = new LaunchActivityItem();
}
- setValues(instance, activityToken, intent, ident, info, curConfig, overrideConfig, deviceId,
- referrer, voiceInteractor, procState, state, persistentState, pendingResults,
- pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
- activityClientController, shareableActivityToken,
+ setValues(instance, activityToken, new Intent(intent), ident, new ActivityInfo(info),
+ new Configuration(curConfig), new Configuration(overrideConfig), deviceId,
+ referrer, voiceInteractor, procState,
+ state != null ? new Bundle(state) : null,
+ persistentState != null ? new PersistableBundle(persistentState) : null,
+ pendingResults != null ? new ArrayList<>(pendingResults) : null,
+ pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
+ activityOptions, isForward,
+ profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
+ assistToken, activityClientController, shareableActivityToken,
launchedFromBubble, taskFragmentToken);
return instance;
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index e56d3f8..961da19 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -69,7 +69,7 @@
}
instance.setActivityToken(activityToken);
instance.mTargetDisplayId = targetDisplayId;
- instance.mConfiguration = configuration;
+ instance.mConfiguration = new Configuration(configuration);
return instance;
}
@@ -78,7 +78,7 @@
public void recycle() {
super.recycle();
mTargetDisplayId = 0;
- mConfiguration = Configuration.EMPTY;
+ mConfiguration = null;
ObjectPool.recycle(this);
}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 8e995aa..acf2ea4 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -32,6 +32,7 @@
import com.android.internal.content.ReferrerIntent;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -71,7 +72,7 @@
instance = new NewIntentItem();
}
instance.setActivityToken(activityToken);
- instance.mIntents = intents;
+ instance.mIntents = new ArrayList<>(intents);
instance.mResume = resume;
return instance;
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index 375d1bf..cbad92f 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -65,7 +65,7 @@
instance = new WindowContextInfoChangeItem();
}
instance.mClientToken = requireNonNull(clientToken);
- instance.mInfo = new WindowContextInfo(config, displayId);
+ instance.mInfo = new WindowContextInfo(new Configuration(config), displayId);
return instance;
}
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 9828133..7d3eb87 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -77,10 +77,10 @@
instance = new WindowStateResizeItem();
}
instance.mWindow = requireNonNull(window);
- instance.mFrames = requireNonNull(frames);
+ instance.mFrames = new ClientWindowFrames(frames);
instance.mReportDraw = reportDraw;
- instance.mConfiguration = requireNonNull(configuration);
- instance.mInsetsState = requireNonNull(insetsState);
+ instance.mConfiguration = new MergedConfiguration(configuration);
+ instance.mInsetsState = new InsetsState(insetsState);
instance.mForceLayout = forceLayout;
instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
instance.mDisplayId = displayId;
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index d0b4fe4..f1ca086 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -35,6 +35,8 @@
import android.service.wearable.WearableSensingService;
import android.system.OsConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -105,7 +107,9 @@
STATUS_SERVICE_UNAVAILABLE,
STATUS_WEARABLE_UNAVAILABLE,
STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusCode {}
private final Context mContext;
private final IWearableSensingManager mService;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 9ea3dfc..e0ce917 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -33,6 +33,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -795,9 +796,19 @@
@UserHandleAware
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public @NonNull List<AssociationInfo> getAllAssociations() {
+ return getAllAssociations(mContext.getUserId());
+ }
+
+ /**
+ * Per-user version of {@link #getAllAssociations()}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public @NonNull List<AssociationInfo> getAllAssociations(@UserIdInt int userId) {
if (!checkFeaturePresent()) return Collections.emptyList();
try {
- return mService.getAllAssociationsForUser(mContext.getUserId());
+ return mService.getAllAssociationsForUser(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -830,12 +841,25 @@
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public void addOnAssociationsChangedListener(
@NonNull Executor executor, @NonNull OnAssociationsChangedListener listener) {
+ addOnAssociationsChangedListener(executor, listener, mContext.getUserId());
+ }
+
+ /**
+ * Per-user version of
+ * {@link #addOnAssociationsChangedListener(Executor, OnAssociationsChangedListener)}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public void addOnAssociationsChangedListener(
+ @NonNull Executor executor, @NonNull OnAssociationsChangedListener listener,
+ @UserIdInt int userId) {
if (!checkFeaturePresent()) return;
synchronized (mListeners) {
final OnAssociationsChangedListenerProxy proxy = new OnAssociationsChangedListenerProxy(
executor, listener);
try {
- mService.addOnAssociationsChangedListener(proxy, mContext.getUserId());
+ mService.addOnAssociationsChangedListener(proxy, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index b3ea93b..60965a8 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -149,6 +149,19 @@
@SystemApi
public static final int LAUNCH_FAILURE_NO_ACTIVITY = 2;
+ /**
+ * Persistent device identifier corresponding to the default device.
+ *
+ * @see Context#DEVICE_ID_DEFAULT
+ * @see VirtualDevice#getPersistentDeviceId()
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API)
+ public static final String PERSISTENT_DEVICE_ID_DEFAULT =
+ "default:" + Context.DEVICE_ID_DEFAULT;
+
private final IVirtualDeviceManager mService;
private final Context mContext;
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index cfab9eb..02066fa 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -58,6 +58,13 @@
}
flag {
+ name: "persistent_device_id_api"
+ namespace: "virtual_devices"
+ description: "Enable persistent device ID notification API"
+ bug: "295258915"
+}
+
+flag {
name: "express_metrics"
namespace: "virtual_devices"
description: "Enable express metrics in VDM"
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 62630c8..d274792 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -163,6 +163,7 @@
GRAMMATICAL_GENDER_FEMININE,
GRAMMATICAL_GENDER_MASCULINE,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface GrammaticalGender {}
/**
@@ -698,6 +699,7 @@
ORIENTATION_LANDSCAPE,
ORIENTATION_SQUARE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface Orientation {
}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 33f602b..c59d3ce 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -163,7 +163,7 @@
* {@link IllegalStateException} if a transaction is not in progress. Clients should call
* {@link SQLiteDatabase.createRawStatement} to create a new instance.
*/
- SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) throws SQLiteException {
+ SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) {
mThread = Thread.currentThread();
mDatabase = db;
mSession = mDatabase.getThreadSession();
@@ -245,7 +245,7 @@
* @throws SQLiteDatabaseLockedException if the database is locked or busy.
* @throws SQLiteException if a native error occurs.
*/
- public boolean step() throws SQLiteException {
+ public boolean step() {
throwIfInvalid();
try {
int err = nativeStep(mStatement, true);
@@ -392,7 +392,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value) throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -418,8 +418,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length)
- throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length) {
Objects.requireNonNull(value);
throwIfInvalid();
throwIfInvalidBounds(value.length, offset, length);
@@ -442,7 +441,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindDouble(int parameterIndex, double value) throws SQLiteException {
+ public void bindDouble(int parameterIndex, double value) {
throwIfInvalid();
try {
nativeBindDouble(mStatement, parameterIndex, value);
@@ -462,7 +461,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindInt(int parameterIndex, int value) throws SQLiteException {
+ public void bindInt(int parameterIndex, int value) {
throwIfInvalid();
try {
nativeBindInt(mStatement, parameterIndex, value);
@@ -482,7 +481,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindLong(int parameterIndex, long value) throws SQLiteException {
+ public void bindLong(int parameterIndex, long value) {
throwIfInvalid();
try {
nativeBindLong(mStatement, parameterIndex, value);
@@ -502,7 +501,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindNull(int parameterIndex) throws SQLiteException {
+ public void bindNull(int parameterIndex) {
throwIfInvalid();
try {
nativeBindNull(mStatement, parameterIndex);
@@ -523,7 +522,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindText(int parameterIndex, @NonNull String value) throws SQLiteException {
+ public void bindText(int parameterIndex, @NonNull String value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -562,7 +561,7 @@
* @throws SQLiteException if a native error occurs.
*/
@SQLiteDataType
- public int getColumnType(int columnIndex) throws SQLiteException {
+ public int getColumnType(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnType(mStatement, columnIndex);
@@ -584,7 +583,7 @@
* @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
*/
@NonNull
- public String getColumnName(int columnIndex) throws SQLiteException {
+ public String getColumnName(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnName(mStatement, columnIndex);
@@ -609,7 +608,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnLength(int columnIndex) throws SQLiteException {
+ public int getColumnLength(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBytes(mStatement, columnIndex);
@@ -635,7 +634,7 @@
* @throws SQLiteException if a native error occurs.
*/
@Nullable
- public byte[] getColumnBlob(int columnIndex) throws SQLiteException {
+ public byte[] getColumnBlob(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBlob(mStatement, columnIndex);
@@ -668,8 +667,7 @@
* @throws SQLiteException if a native error occurs.
*/
public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
- int length, int srcOffset)
- throws SQLiteException {
+ int length, int srcOffset) {
Objects.requireNonNull(buffer);
throwIfInvalid();
throwIfInvalidBounds(buffer.length, offset, length);
@@ -695,7 +693,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public double getColumnDouble(int columnIndex) throws SQLiteException {
+ public double getColumnDouble(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnDouble(mStatement, columnIndex);
@@ -719,7 +717,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnInt(int columnIndex) throws SQLiteException {
+ public int getColumnInt(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnInt(mStatement, columnIndex);
@@ -743,7 +741,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public long getColumnLong(int columnIndex) throws SQLiteException {
+ public long getColumnLong(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnLong(mStatement, columnIndex);
@@ -768,7 +766,7 @@
* @throws SQLiteException if a native error occurs.
*/
@NonNull
- public String getColumnText(int columnIndex) throws SQLiteException {
+ public String getColumnText(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnText(mStatement, columnIndex);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 90bbca8..f82f79e 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -131,6 +131,7 @@
BIOMETRIC_CONVENIENCE,
DEVICE_CREDENTIAL,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface Types {}
/**
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index aca6d06..5d06978 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3402,8 +3402,8 @@
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -3464,6 +3464,10 @@
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1c66f82..0d204f3 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4103,8 +4103,8 @@
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -4165,6 +4165,10 @@
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
index 8304796..cf496d2 100644
--- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
+++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
@@ -90,6 +90,22 @@
break;
}
}
+
+ if (!removeError) {
+ // Check for the case where we might have an error after a frame number gap
+ // caused by other types of capture requests
+ int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
+ int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
+ if (mPendingFrameNumbersWithOtherType[otherType1].isEmpty() &&
+ mPendingFrameNumbersWithOtherType[otherType2].isEmpty()) {
+ long errorGapNumber = Math.max(mCompletedFrameNumber[otherType1],
+ mCompletedFrameNumber[otherType2]) + 1;
+ if ((errorGapNumber > mCompletedFrameNumber[requestType] + 1) &&
+ (errorGapNumber == errorFrameNumber)) {
+ removeError = true;
+ }
+ }
+ }
}
if (removeError) {
mCompletedFrameNumber[requestType] = errorFrameNumber;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b0b7a41..440585c 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -159,6 +159,7 @@
RESULT_INCORRECT_MODE,
RESULT_COMMUNICATION_FAILED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ControlCallbackResult {}
/** Control operation is successfully handled by the framework. */
@@ -1135,6 +1136,7 @@
CEC_SETTING_NAME_QUERY_SAD_MAX,
SETTING_NAME_EARC_ENABLED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SettingName {}
/**
@@ -1157,6 +1159,7 @@
CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
CEC_SETTING_NAME_QUERY_SAD_MAX,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CecSettingSad {}
// True if we have a logical device of type playback hosted in the system.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 889d3df..81a0234 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -52,6 +52,8 @@
import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -738,6 +740,7 @@
FUNCTION_NCM,
FUNCTION_UVC,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbFunctionMode {}
/** @hide */
@@ -748,6 +751,7 @@
GADGET_HAL_V1_2,
GADGET_HAL_V2_0,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbGadgetHalVersion {}
/** @hide */
@@ -759,6 +763,7 @@
USB_HAL_V1_3,
USB_HAL_V2_0,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface UsbHalVersion {}
/**
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0c95c2e..f6beec1 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -84,4 +84,6 @@
boolean isReaderOptionSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
boolean enableReaderOption(boolean enable);
+ boolean isObserveModeSupported();
+ boolean setObserveMode(boolean enabled);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index c7b3b2c..191385a 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -30,6 +30,7 @@
boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
+ boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c897595..98a980f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1081,6 +1081,61 @@
}
}
+
+ /**
+ * Returns whether the device supports observer mode or not. When observe
+ * mode is enabled, the NFC hardware will listen for NFC readers, but not
+ * respond to them. When observe mode is disabled, the NFC hardware will
+ * resoond to the reader and proceed with the transaction.
+ * @return true if the mode is supported, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean isObserveModeSupported() {
+ try {
+ return sService.isObserveModeSupported();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Disables observe mode to allow the transaction to proceed. See
+ * {@link #isObserveModeSupported()} for a description of observe mode and
+ * use {@link #disallowTransaction()} to enable observe mode and block
+ * transactions again.
+ *
+ * @return boolean indicating success or failure.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean allowTransaction() {
+ try {
+ return sService.setObserveMode(false);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Signals that the transaction has completed and observe mode may be
+ * reenabled. See {@link #isObserveModeSupported()} for a description of
+ * observe mode and use {@link #allowTransaction()} to disable observe
+ * mode and allow transactions to proceed.
+ *
+ * @return boolean indicating success or failure.
+ */
+
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean disallowTransaction() {
+ try {
+ return sService.setObserveMode(true);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
/**
* Resumes default polling for the current device state if polling is paused. Calling
* this while polling is not paused is a no-op.
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d048b59..58b6179 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -328,6 +328,24 @@
return SELECTION_MODE_ASK_IF_CONFLICT;
}
}
+ /**
+ * Sets whether the system should default to observe mode or not when
+ * the service is in the foreground or the default payment service.
+ *
+ * @param service The component name of the service
+ * @param enable Whether the servic should default to observe mode or not
+ * @return whether the change was successful.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+ try {
+ return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ service, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ }
+ return false;
+ }
/**
* Registers a list of AIDs for a specific category for the
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index 55d0e73..7cd2533 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -16,11 +16,14 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -29,6 +32,9 @@
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>HostApduService is a convenience {@link Service} class that can be
* extended to emulate an NFC card inside an Android
@@ -230,9 +236,99 @@
/**
* @hide
*/
+ public static final int MSG_POLLING_LOOP = 4;
+
+ /**
+ * @hide
+ */
public static final String KEY_DATA = "data";
/**
+ * POLLING_LOOP_TYPE_KEY is the Bundle key for the type of
+ * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_A = 'A';
+
+ /**
+ * POLLING_LOOP_TYPE_B is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_B = 'B';
+
+ /**
+ * POLLING_LOOP_TYPE_F is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_F = 'F';
+
+ /**
+ * POLLING_LOOP_TYPE_ON is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_ON = 'O';
+
+ /**
+ * POLLING_LOOP_TYPE_OFF is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_OFF = 'X';
+
+ /**
+ * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+
+ /**
+ * POLLING_LOOP_GAIN_KEY is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * POLLING_LOOP_TIMESTAMP_KEY is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+
+ /**
+ * @hide
+ */
+ public static final String POLLING_LOOP_FRAMES_BUNDLE_KEY =
+ "android.nfc.cardemulation.POLLING_FRAMES";
+
+ /**
* Messenger interface to NfcService for sending responses.
* Only accessed on main thread by the message handler.
*
@@ -255,6 +351,7 @@
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
+ HostApduService has = HostApduService.this;
byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
@@ -306,6 +403,12 @@
Log.e(TAG, "RemoteException calling into NfcService.");
}
break;
+ case MSG_POLLING_LOOP:
+ ArrayList<Bundle> frames =
+ msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+ Bundle.class);
+ processPollingFrames(frames);
+ break;
default:
super.handleMessage(msg);
}
@@ -366,6 +469,21 @@
}
}
+ /**
+ * This method is called when a polling frame has been received from a
+ * remote device. If the device is in observe mode, the service should
+ * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
+ * with the transaction. If the device is not in observe mode, the service
+ * can use this polling frame information to determine how to proceed if it
+ * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The
+ * service must override this method inorder to receive polling frames,
+ * otherwise the base implementation drops the frame.
+ *
+ * @param frame A description of the polling frame.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void processPollingFrames(@NonNull List<Bundle> frame) {
+ }
/**
* <p>This method will be called when a command APDU has been received
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index cd50ace..17e0427 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -20,3 +20,31 @@
description: "Flag for NFC user restriction"
bug: "291187960"
}
+
+flag {
+ name: "nfc_observe_mode"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_observe_mode_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode ST shim"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications ST shim"
+ bug: "294217286"
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 9956220..0ccc485 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -30,9 +30,11 @@
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -46,6 +48,7 @@
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.function.Supplier;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -289,6 +292,33 @@
sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking);
}
+ private static ThreadLocal<SomeArgs> sIdentity$ravenwood;
+
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
+ private static class IdentitySupplier implements Supplier<SomeArgs> {
+ @Override
+ public SomeArgs get() {
+ final SomeArgs args = SomeArgs.obtain();
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.FALSE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return args;
+ }
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood() {
+ sIdentity$ravenwood = ThreadLocal.withInitial(new IdentitySupplier());
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
*/
@@ -312,8 +342,14 @@
* Warning: oneway transactions do not receive PID.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingPid();
+ /** @hide */
+ public static final int getCallingPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
+ }
+
/**
* Return the Linux UID assigned to the process that sent you the
* current transaction that is being processed. This UID can be used with
@@ -322,8 +358,14 @@
* incoming transaction, then its own UID is returned.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingUid();
+ /** @hide */
+ public static final int getCallingUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
+ }
+
/**
* Returns {@code true} if the current thread is currently executing an
* incoming transaction.
@@ -331,6 +373,7 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native boolean isDirectlyHandlingTransactionNative();
/** @hide */
@@ -344,6 +387,7 @@
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isDirectlyHandlingTransaction() {
return sIsHandlingBinderTransaction || isDirectlyHandlingTransactionNative();
}
@@ -363,8 +407,15 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native boolean hasExplicitIdentity();
+ /** @hide */
+ private static boolean hasExplicitIdentity$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().arg1
+ == Boolean.TRUE;
+ }
+
/**
* Return the Linux UID assigned to the process that sent the transaction
* currently being processed.
@@ -373,6 +424,7 @@
* executing an incoming transaction and the calling identity has not been
* explicitly set with {@link #clearCallingIdentity()}
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int getCallingUidOrThrow() {
if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
throw new IllegalStateException(
@@ -434,8 +486,26 @@
* @see #restoreCallingIdentity(long)
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native long clearCallingIdentity();
+ /** @hide */
+ public static final long clearCallingIdentity$ravenwood() {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ long res = ((long) args.argi1 << 32) | args.argi2;
+ if (args.arg1 == Boolean.TRUE) {
+ res |= (0x1 << 30);
+ } else {
+ res &= ~(0x1 << 30);
+ }
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.TRUE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return res;
+ }
+
/**
* Restore the identity of the incoming IPC on the current thread
* back to a previously identity that was returned by {@link
@@ -447,8 +517,18 @@
* @see #clearCallingIdentity
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void restoreCallingIdentity(long token);
+ /** @hide */
+ public static final void restoreCallingIdentity$ravenwood(long token) {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ args.arg1 = ((token & (0x1 << 30)) != 0) ? Boolean.TRUE : Boolean.FALSE;
+ args.argi1 = (int) (token >> 32);
+ args.argi2 = (int) (token & ~(0x1 << 30));
+ }
+
/**
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
@@ -644,8 +724,14 @@
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void flushPendingCommands();
+ /** @hide */
+ public static final void flushPendingCommands$ravenwood() {
+ // Ravenwood doesn't support IPC; ignored
+ }
+
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -703,6 +789,7 @@
* <p>If you're creating a Binder token (a Binder object without an attached interface),
* you should use {@link #Binder(String)} instead.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder() {
this(null);
}
@@ -719,6 +806,7 @@
* Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
* help identify them.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
if (mObject != 0L) {
@@ -742,6 +830,7 @@
* will be implemented for you to return the given owner IInterface when
* the corresponding descriptor is requested.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
@@ -750,6 +839,7 @@
/**
* Default implementation returns an empty interface name.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getInterfaceDescriptor() {
return mDescriptor;
}
@@ -758,6 +848,7 @@
* Default implementation always returns true -- if you got here,
* the object is alive.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean pingBinder() {
return true;
}
@@ -768,6 +859,7 @@
* Note that if you're calling on a local binder, this always returns true
* because your process is alive if you're calling it.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isBinderAlive() {
return true;
}
@@ -777,6 +869,7 @@
* to return the associated {@link IInterface} if it matches the requested
* descriptor.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
@@ -1250,12 +1343,14 @@
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}
@@ -1283,6 +1378,7 @@
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long getNativeBBinderHolder();
private static long getNativeBBinderHolder$ravenwood() {
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 90e4b17..91c2965 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -194,6 +194,7 @@
* Limit that should be placed on IPC sizes, in bytes, to keep them safely under the transaction
* buffer limit.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
static int getSuggestedMaxIpcSizeBytes() {
return MAX_IPC_SIZE;
}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index f30dd20..0f27569 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -33,13 +33,13 @@
boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibration vibration, in VibrationAttributes attributes);
- void vibrate(int uid, int displayId, String opPkg, in CombinedVibration vibration,
+ void vibrate(int uid, int deviceId, String opPkg, in CombinedVibration vibration,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(int usageFilter, IBinder token);
// Async oneway APIs.
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
- oneway void performHapticFeedback(int uid, int displayId, String opPkg, int constant,
+ oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
boolean always, String reason, IBinder token);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 677143a..daec1721 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,6 +34,9 @@
import android.util.Pair;
import android.webkit.WebViewZygote;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
+
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -833,14 +836,37 @@
return VMRuntime.getRuntime().is64Bit();
}
+ private static SomeArgs sIdentity$ravenwood;
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood(int uid, int pid) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = uid;
+ args.argi2 = pid;
+ sIdentity$ravenwood = args;
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Returns the identifier of this process, which can be used with
* {@link #killProcess} and {@link #sendSignal}.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myPid() {
return Os.getpid();
}
+ /** @hide */
+ public static final int myPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi2;
+ }
+
/**
* Returns the identifier of this process' parent.
* @hide
@@ -864,10 +890,16 @@
* app-specific sandbox. It is different from {@link #myUserHandle} in that
* a uid identifies a specific app sandbox in a specific user.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myUid() {
return Os.getuid();
}
+ /** @hide */
+ public static final int myUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi1;
+ }
+
/**
* Returns this process's user handle. This is the
* user the process is running under. It is distinct from
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index d4688f8..f71c269 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -57,6 +57,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.SignatureException;
@@ -166,6 +168,7 @@
RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ResumeOnRebootRebootErrorCode {}
/**
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 831ca86..49a0bd3 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.location.ILocationManager;
import android.location.LocationTime;
+import android.text.format.DateUtils;
import android.util.Slog;
import dalvik.annotation.optimization.CriticalNative;
@@ -125,6 +126,7 @@
*
* @param ms to sleep before returning, in milliseconds of uptime.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void sleep(long ms)
{
long start = uptimeMillis();
@@ -186,8 +188,16 @@
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long uptimeMillis();
+ /** @hide */
+ public static long uptimeMillis$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000);
+ }
+
/**
* Returns nanoseconds since boot, not counting time spent in deep sleep.
*
@@ -195,8 +205,16 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long uptimeNanos();
+ /** @hide */
+ public static long uptimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000_000_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, not counting time spent
* in deep sleep.
@@ -218,8 +236,15 @@
* @return elapsed milliseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long elapsedRealtime();
+ /** @hide */
+ public static long elapsedRealtime$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, including time spent in
* sleep.
@@ -241,8 +266,15 @@
* @return elapsed nanoseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long elapsedRealtimeNanos();
+ /** @hide */
+ public static long elapsedRealtimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000);
+ }
+
/**
* Returns milliseconds running in the current thread.
*
@@ -271,8 +303,15 @@
*/
@UnsupportedAppUsage
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long currentTimeMicro();
+ /** @hide */
+ public static long currentTimeMicro$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() / 1000L;
+ }
+
/**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index ee90834..bc85412e 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -137,8 +137,8 @@
return;
}
try {
- mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
- reason, mToken);
+ mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -152,8 +152,8 @@
}
try {
mService.performHapticFeedback(
- Process.myUid(), mContext.getAssociatedDisplayId(), mPackageName, constant,
- always, reason, mToken);
+ Process.myUid(), mContext.getDeviceId(), mPackageName, constant, always, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index b7e3068..0a8f62f 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -25,6 +25,9 @@
import android.os.IUpdateEngineCallback;
import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* UpdateEngine handles calls to the update engine which takes care of A/B OTA
* updates. It wraps up the update engine Binder APIs and exposes them as
@@ -178,6 +181,7 @@
ErrorCodeConstants.NOT_ENOUGH_SPACE,
ErrorCodeConstants.DEVICE_CORRUPTED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ErrorCode {}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2d1802a..6853892 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2133,6 +2133,7 @@
MOUNT_MODE_EXTERNAL_PASS_THROUGH,
MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE
})
+ @Retention(RetentionPolicy.SOURCE)
/** @hide */
public @interface MountMode {}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 7369740..fb2ac711 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -44,6 +44,13 @@
}
flag {
+ name: "enhanced_confirmation_mode_apis"
+ namespace: "permissions"
+ description: "enable enhanced confirmation mode apis"
+ bug: "310220212"
+}
+
+flag {
name: "op_enable_mobile_data_by_user"
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c012ff3..4e7734c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19039,6 +19039,14 @@
public static final int BATTERY_SAVER_MODE_CUSTOM = 4;
/**
+ Whether 1P apps vote for enabling data during different modes,
+ i.e. BTM, BBSM
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final String CONNECTIVITY_KEEP_DATA_ON = "wear_connectivity_keep_data_on";
+
+ /**
* The maximum ambient mode duration when an activity is allowed to auto resume.
* @hide
*/
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index bcda25a..72c436e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -17,6 +17,7 @@
package android.provider;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -49,6 +50,7 @@
import android.util.Patterns;
import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -3191,8 +3193,8 @@
* Sets whether the PDU session brought up by this APN should always be on.
* See 3GPP TS 23.501 section 5.6.13
* <P>Type: INTEGER</P>
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String ALWAYS_ON = "always_on";
/**
@@ -3302,18 +3304,16 @@
* The MTU (maximum transmit unit) size of the mobile interface for IPv4 to which the APN is
* connected, in bytes.
* <p>Type: INTEGER </p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String MTU_V4 = "mtu_v4";
/**
* The MTU (maximum transmit unit) size of the mobile interface for IPv6 to which the APN is
* connected, in bytes.
* <p>Type: INTEGER </p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String MTU_V6 = "mtu_v6";
/**
@@ -3335,17 +3335,15 @@
/**
* {@code true} if this APN visible to the user, {@code false} otherwise.
* <p>Type: INTEGER (boolean)</p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String USER_VISIBLE = "user_visible";
/**
* {@code true} if the user allowed to edit this APN, {@code false} otherwise.
* <p>Type: INTEGER (boolean)</p>
- * @hide
*/
- @SystemApi
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public static final String USER_EDITABLE = "user_editable";
/**
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 759953e..92c516c 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -309,6 +309,7 @@
REASON_ASSISTANT_CANCEL,
REASON_LOCKDOWN,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationCancelReason{};
/**
@@ -320,6 +321,7 @@
FLAG_FILTER_TYPE_SILENT,
FLAG_FILTER_TYPE_ONGOING
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface NotificationFilterTypes {}
/**
* A flag value indicating that this notification listener can see conversation type
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
new file mode 100644
index 0000000..5b096c6
--- /dev/null
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -0,0 +1,360 @@
+/*
+ * 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.service.notification;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * Represents the set of device effects (affecting display and device behavior in general) that
+ * are applied whenever an {@link android.app.AutomaticZenRule} is active.
+ */
+@FlaggedApi(Flags.FLAG_MODES_API)
+public final class ZenDeviceEffects implements Parcelable {
+
+ private final boolean mGrayscale;
+ private final boolean mSuppressAmbientDisplay;
+ private final boolean mDimWallpaper;
+ private final boolean mNightMode;
+
+ private final boolean mDisableAutoBrightness;
+ private final boolean mDisableTapToWake;
+ private final boolean mDisableTiltToWake;
+ private final boolean mDisableTouch;
+ private final boolean mMinimizeRadioUsage;
+ private final boolean mMaximizeDoze;
+
+ private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
+ boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
+ boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
+ boolean minimizeRadioUsage, boolean maximizeDoze) {
+ mGrayscale = grayscale;
+ mSuppressAmbientDisplay = suppressAmbientDisplay;
+ mDimWallpaper = dimWallpaper;
+ mNightMode = nightMode;
+ mDisableAutoBrightness = disableAutoBrightness;
+ mDisableTapToWake = disableTapToWake;
+ mDisableTiltToWake = disableTiltToWake;
+ mDisableTouch = disableTouch;
+ mMinimizeRadioUsage = minimizeRadioUsage;
+ mMaximizeDoze = maximizeDoze;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof final ZenDeviceEffects that)) return false;
+ if (obj == this) return true;
+
+ return this.mGrayscale == that.mGrayscale
+ && this.mSuppressAmbientDisplay == that.mSuppressAmbientDisplay
+ && this.mDimWallpaper == that.mDimWallpaper
+ && this.mNightMode == that.mNightMode
+ && this.mDisableAutoBrightness == that.mDisableAutoBrightness
+ && this.mDisableTapToWake == that.mDisableTapToWake
+ && this.mDisableTiltToWake == that.mDisableTiltToWake
+ && this.mDisableTouch == that.mDisableTouch
+ && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
+ && this.mMaximizeDoze == that.mMaximizeDoze;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
+ mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
+ mMinimizeRadioUsage, mMaximizeDoze);
+ }
+
+ @Override
+ public String toString() {
+ ArrayList<String> effects = new ArrayList<>(10);
+ if (mGrayscale) effects.add("grayscale");
+ if (mSuppressAmbientDisplay) effects.add("suppressAmbientDisplay");
+ if (mDimWallpaper) effects.add("dimWallpaper");
+ if (mNightMode) effects.add("nightMode");
+ if (mDisableAutoBrightness) effects.add("disableAutoBrightness");
+ if (mDisableTapToWake) effects.add("disableTapToWake");
+ if (mDisableTiltToWake) effects.add("disableTiltToWake");
+ if (mDisableTouch) effects.add("disableTouch");
+ if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
+ if (mMaximizeDoze) effects.add("maximizeDoze");
+ return "[" + String.join(", ", effects) + "]";
+ }
+
+ /**
+ * Whether the level of color saturation of the display should be set to minimum, effectively
+ * switching it to grayscale, while the rule is active.
+ */
+ public boolean shouldDisplayGrayscale() {
+ return mGrayscale;
+ }
+
+ /**
+ * Whether the ambient (always-on) display feature should be disabled while the rule is active.
+ * This will have no effect if the device doesn't support always-on display or if it's not
+ * generally enabled.
+ */
+ public boolean shouldSuppressAmbientDisplay() {
+ return mSuppressAmbientDisplay;
+ }
+
+ /** Whether the wallpaper should be dimmed while the rule is active. */
+ public boolean shouldDimWallpaper() {
+ return mDimWallpaper;
+ }
+
+ /** Whether night mode (aka dark theme) should be applied while the rule is active. */
+ public boolean shouldUseNightMode() {
+ return mNightMode;
+ }
+
+ /**
+ * Whether the display's automatic brightness adjustment should be disabled while the rule is
+ * active.
+ * @hide
+ */
+ public boolean shouldDisableAutoBrightness() {
+ return mDisableAutoBrightness;
+ }
+
+ /**
+ * Whether "tap to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTapToWake() {
+ return mDisableTapToWake;
+ }
+
+ /**
+ * Whether "tilt to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTiltToWake() {
+ return mDisableTiltToWake;
+ }
+
+ /**
+ * Whether touch interactions should be disabled while the rule is active.
+ * @hide
+ */
+ public boolean shouldDisableTouch() {
+ return mDisableTouch;
+ }
+
+ /**
+ * Whether radio (wi-fi, LTE, etc) traffic, and its attendant battery consumption, should be
+ * minimized while the rule is active.
+ * @hide
+ */
+ public boolean shouldMinimizeRadioUsage() {
+ return mMinimizeRadioUsage;
+ }
+
+ /**
+ * Whether Doze should be enhanced (e.g. with more aggresive activation, or less frequent
+ * maintenance windows) while the rule is active.
+ * @hide
+ */
+ public boolean shouldMaximizeDoze() {
+ return mMaximizeDoze;
+ }
+
+ /** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */
+ @NonNull
+ public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() {
+ @Override
+ public ZenDeviceEffects createFromParcel(Parcel in) {
+ return new ZenDeviceEffects(in.readBoolean(), in.readBoolean(), in.readBoolean(),
+ in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
+ in.readBoolean(), in.readBoolean(), in.readBoolean());
+ }
+
+ @Override
+ public ZenDeviceEffects[] newArray(int size) {
+ return new ZenDeviceEffects[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mGrayscale);
+ dest.writeBoolean(mSuppressAmbientDisplay);
+ dest.writeBoolean(mDimWallpaper);
+ dest.writeBoolean(mNightMode);
+ dest.writeBoolean(mDisableAutoBrightness);
+ dest.writeBoolean(mDisableTapToWake);
+ dest.writeBoolean(mDisableTiltToWake);
+ dest.writeBoolean(mDisableTouch);
+ dest.writeBoolean(mMinimizeRadioUsage);
+ dest.writeBoolean(mMaximizeDoze);
+ }
+
+ /** Builder class for {@link ZenDeviceEffects} objects. */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final class Builder {
+
+ private boolean mGrayscale;
+ private boolean mSuppressAmbientDisplay;
+ private boolean mDimWallpaper;
+ private boolean mNightMode;
+ private boolean mDisableAutoBrightness;
+ private boolean mDisableTapToWake;
+ private boolean mDisableTiltToWake;
+ private boolean mDisableTouch;
+ private boolean mMinimizeRadioUsage;
+ private boolean mMaximizeDoze;
+
+ /**
+ * Instantiates a new {@link ZenPolicy.Builder} with all effects set to default (disabled).
+ */
+ public Builder() {
+ }
+
+ /**
+ * Instantiates a new {@link ZenPolicy.Builder} with all effects set to their corresponding
+ * values in the supplied {@link ZenDeviceEffects}.
+ */
+ public Builder(@NonNull ZenDeviceEffects zenDeviceEffects) {
+ mGrayscale = zenDeviceEffects.shouldDisplayGrayscale();
+ mSuppressAmbientDisplay = zenDeviceEffects.shouldSuppressAmbientDisplay();
+ mDimWallpaper = zenDeviceEffects.shouldDimWallpaper();
+ mNightMode = zenDeviceEffects.shouldUseNightMode();
+ mDisableAutoBrightness = zenDeviceEffects.shouldDisableAutoBrightness();
+ mDisableTapToWake = zenDeviceEffects.shouldDisableTapToWake();
+ mDisableTiltToWake = zenDeviceEffects.shouldDisableTiltToWake();
+ mDisableTouch = zenDeviceEffects.shouldDisableTouch();
+ mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
+ mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
+ }
+
+ /**
+ * Sets whether the level of color saturation of the display should be set to minimum,
+ * effectively switching it to grayscale, while the rule is active.
+ */
+ @NonNull
+ public Builder setShouldDisplayGrayscale(boolean grayscale) {
+ mGrayscale = grayscale;
+ return this;
+ }
+
+ /**
+ * Sets whether the ambient (always-on) display feature should be disabled while the rule
+ * is active. This will have no effect if the device doesn't support always-on display or if
+ * it's not generally enabled.
+ */
+ @NonNull
+ public Builder setShouldSuppressAmbientDisplay(boolean suppressAmbientDisplay) {
+ mSuppressAmbientDisplay = suppressAmbientDisplay;
+ return this;
+ }
+
+ /** Sets whether the wallpaper should be dimmed while the rule is active. */
+ @NonNull
+ public Builder setShouldDimWallpaper(boolean dimWallpaper) {
+ mDimWallpaper = dimWallpaper;
+ return this;
+ }
+
+ /** Sets whether night mode (aka dark theme) should be applied while the rule is active. */
+ @NonNull
+ public Builder setShouldUseNightMode(boolean nightMode) {
+ mNightMode = nightMode;
+ return this;
+ }
+
+ /**
+ * Sets whether the display's automatic brightness adjustment should be disabled while the
+ * rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableAutoBrightness(boolean disableAutoBrightness) {
+ mDisableAutoBrightness = disableAutoBrightness;
+ return this;
+ }
+
+ /**
+ * Sets whether "tap to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTapToWake(boolean disableTapToWake) {
+ mDisableTapToWake = disableTapToWake;
+ return this;
+ }
+
+ /**
+ * Sets whether "tilt to wake" should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTiltToWake(boolean disableTiltToWake) {
+ mDisableTiltToWake = disableTiltToWake;
+ return this;
+ }
+
+ /**
+ * Sets whether touch interactions should be disabled while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldDisableTouch(boolean disableTouch) {
+ mDisableTouch = disableTouch;
+ return this;
+ }
+
+ /**
+ * Sets whether radio (wi-fi, LTE, etc) traffic, and its attendant battery consumption,
+ * should be minimized while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldMinimizeRadioUsage(boolean minimizeRadioUsage) {
+ mMinimizeRadioUsage = minimizeRadioUsage;
+ return this;
+ }
+
+ /**
+ * Sets whether Doze should be enhanced (e.g. with more aggresive activation, or less
+ * frequent maintenance windows) while the rule is active.
+ * @hide
+ */
+ @NonNull
+ public Builder setShouldMaximizeDoze(boolean maximizeDoze) {
+ mMaximizeDoze = maximizeDoze;
+ return this;
+ }
+
+ /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
+ @NonNull
+ public ZenDeviceEffects build() {
+ return new ZenDeviceEffects(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper,
+ mNightMode, mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake,
+ mDisableTouch, mMinimizeRadioUsage, mMaximizeDoze);
+ }
+ }
+}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index ff6dffd..1e08fd8 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -85,6 +85,7 @@
CONFIDENCE_LEVEL_HIGH,
CONFIDENCE_LEVEL_VERY_HIGH
})
+ @Retention(RetentionPolicy.SOURCE)
@interface HotwordConfidenceLevelValue {
}
diff --git a/core/java/android/service/voice/HotwordRejectedResult.java b/core/java/android/service/voice/HotwordRejectedResult.java
index 7b3f47d..26c1ca4 100644
--- a/core/java/android/service/voice/HotwordRejectedResult.java
+++ b/core/java/android/service/voice/HotwordRejectedResult.java
@@ -22,6 +22,9 @@
import com.android.internal.util.DataClass;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a result supporting the rejected hotword trigger.
*
@@ -57,6 +60,7 @@
CONFIDENCE_LEVEL_MEDIUM,
CONFIDENCE_LEVEL_HIGH
})
+ @Retention(RetentionPolicy.SOURCE)
@interface HotwordConfidenceLevelValue {
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 35834fd..f1c1420 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -38,6 +38,7 @@
import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.CloseGuard;
import android.util.Log;
import android.util.Slog;
@@ -46,6 +47,7 @@
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
@@ -59,6 +61,9 @@
* {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
* invoked only from the main application thread.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p>The implementation of this API is likely to stream audio to remote servers to perform speech
* recognition. As such this API is not intended to be used for continuous recognition, which would
* consume a significant amount of battery and bandwidth.
@@ -301,6 +306,8 @@
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
/** Context with which the manager was created */
private final Context mContext;
@@ -366,9 +373,7 @@
* {@link #createSpeechRecognizer} static factory method
*/
private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
- mContext = context;
- mServiceComponent = serviceComponent;
- mOnDevice = false;
+ this(context, serviceComponent, false);
}
/**
@@ -376,9 +381,17 @@
* {@link #createOnDeviceSpeechRecognizer} static factory method
*/
private SpeechRecognizer(final Context context, boolean onDevice) {
+ this(context, null, onDevice);
+ }
+
+ private SpeechRecognizer(
+ final Context context,
+ final ComponentName serviceComponent,
+ final boolean onDevice) {
mContext = context;
- mServiceComponent = null;
+ mServiceComponent = serviceComponent;
mOnDevice = onDevice;
+ mCloseGuard.open("SpeechRecognizer#destroy()");
}
/**
@@ -418,6 +431,9 @@
* command to the created {@code SpeechRecognizer}, otherwise no notifications will be
* received.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p>For apps targeting Android 11 (API level 30) interaction with a speech recognition
* service requires <queries> element to be added to the manifest file:
* <pre>{@code
@@ -445,6 +461,9 @@
* Use this version of the method to specify a specific service to direct this
* {@link SpeechRecognizer} to.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* <p><strong>Important</strong>: before calling this method, please check via
* {@link android.content.pm.PackageManager#queryIntentServices(Intent, int)} that {@code
* serviceComponent} actually exists and provides
@@ -486,6 +505,9 @@
* before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
* notifications will be received.
*
+ * <p><strong>Important:</strong> the caller MUST invoke {@link #destroy()} on a
+ * SpeechRecognizer object when it is no longer needed.
+ *
* @param context in which to create {@code SpeechRecognizer}
* @return a new on-device {@code SpeechRecognizer}.
* @throws UnsupportedOperationException iff {@link #isOnDeviceRecognitionAvailable(Context)}
@@ -886,17 +908,32 @@
/** Destroys the {@code SpeechRecognizer} object. */
public void destroy() {
- if (mService != null) {
- try {
- mService.cancel(mListener, /*isShutdown*/ true);
- } catch (final Exception e) {
- // Not important
+ try {
+ if (mService != null) {
+ try {
+ mService.cancel(mListener, /*isShutdown*/ true);
+ } catch (final Exception e) {
+ // Not important
+ }
}
- }
- mService = null;
- mPendingTasks.clear();
- mListener.mInternalListener = null;
+ mService = null;
+ mPendingTasks.clear();
+ mListener.mInternalListener = null;
+ mCloseGuard.close();
+ } finally {
+ Reference.reachabilityFence(this);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ destroy();
+ } finally {
+ super.finalize();
+ }
}
/** Establishes a connection to system server proxy and initializes the session. */
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 766e924..b91a878 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -163,12 +163,6 @@
public static final String SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS =
"settings_remoteauth_enrollment";
- /** Flag to enable/disable entire page in Accessibility -> Hearing aids
- * @hide
- */
- public static final String SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE =
- "settings_accessibility_hearing_aid_page";
-
/**
* Flag to enable/disable preferring the AccessibilityMenu service in the system.
* @hide
@@ -244,7 +238,6 @@
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
- DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, "true");
DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c735142..d4bd9e5 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -651,6 +651,7 @@
REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY,
REMOVE_CONTENT_MODE_DESTROY,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface RemoveContentMode {}
/**
@@ -685,6 +686,7 @@
DISPLAY_IME_POLICY_FALLBACK_DISPLAY,
DISPLAY_IME_POLICY_HIDE,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface DisplayImePolicy {}
/**
@@ -3349,6 +3351,7 @@
PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface PrivateFlags {}
/**
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
index c4d43bc..eb2a101 100644
--- a/core/java/android/view/inputmethod/HandwritingGesture.java
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -86,6 +86,7 @@
* Granular level on which text should be operated.
*/
@IntDef({GRANULARITY_CHARACTER, GRANULARITY_WORD})
+ @Retention(RetentionPolicy.SOURCE)
@interface Granularity {}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index eeab005..589b7a3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1176,24 +1176,33 @@
mActive = interactive;
mFullscreenMode = fullscreen;
if (interactive) {
+ // Find the next view focus to start the input connection when the
+ // device was interactive.
final View rootView =
mCurRootView != null ? mCurRootView.getView() : null;
if (rootView == null) {
+ // No window focused or view was removed, ignore request.
return;
}
- // Find the next view focus to start the input connection when the
- // device was interactive.
final ViewRootImpl currentViewRootImpl = mCurRootView;
+ // Post this on UI thread as required for view focus code.
rootView.post(() -> {
synchronized (mH) {
if (mCurRootView != currentViewRootImpl) {
+ // Focused window changed since posting, ignore request.
return;
}
}
- final View focusedView = currentViewRootImpl.getView().findFocus();
+ final View curRootView = currentViewRootImpl.getView();
+ if (curRootView == null) {
+ // View was removed, ignore request.
+ return;
+ }
+ final View focusedView = curRootView.findFocus();
onViewFocusChangedInternal(focusedView, focusedView != null);
});
} else {
+ // Finish input connection when device becomes non-interactive.
finishInputLocked();
if (isImeSessionAvailableLocked()) {
mCurBindState.mImeSession.finishInput();
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index e44f436..4816f35 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -27,6 +27,9 @@
import android.os.Parcelable;
import android.os.RemoteCallback;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Information to be sent to SysUI about a back event.
*
@@ -85,6 +88,7 @@
TYPE_CROSS_TASK,
TYPE_CALLBACK
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface BackTargetType {
}
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 0ce076b6..1bd921b 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* The window frame container class used by client side for layout.
* @hide
@@ -101,6 +103,29 @@
}
@Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ClientWindowFrames other = (ClientWindowFrames) o;
+ return frame.equals(other.frame)
+ && displayFrame.equals(other.displayFrame)
+ && parentFrame.equals(other.parentFrame)
+ && Objects.equals(attachedFrame, other.attachedFrame)
+ && isParentFrameClippedByDisplayCutout == other.isParentFrameClippedByDisplayCutout
+ && compatScale == other.compatScale;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(frame, displayFrame, parentFrame, attachedFrame,
+ isParentFrameClippedByDisplayCutout, compatScale);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index f1c0d8d..b6c04d9 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -33,6 +33,8 @@
import android.util.Singleton;
import android.util.Slog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -65,6 +67,7 @@
SPLASH_SCREEN_STYLE_SOLID_COLOR,
SPLASH_SCREEN_STYLE_ICON
})
+ @Retention(RetentionPolicy.SOURCE)
@interface SplashScreenStyle {}
/**
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index b63a969..9fe30df 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -6,4 +6,12 @@
description: "When necessary, configuration decoupled from status bar and display cutout"
bug: "291870756"
is_fixed_read_only: true
+}
+
+flag {
+ name: "movable_cutout_configuration"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Make it possible to move cutout across edges through device config"
+ bug: "302387383"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 0ad6c99..b600b22 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -35,4 +35,11 @@
name: "fullscreen_dim_flag"
description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
bug: "253533308"
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "activity_embedding_interactive_divider_flag"
+ description: "Whether the interactive divider feature is enabled"
+ bug: "293654166"
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
similarity index 75%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
index 1826f7a..b0f3578 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,19 +26,6 @@
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface ParsedActivity extends ParsedMainComponent {
- /**
- * Generate activity object that forwards user to App Details page automatically.
- * This activity should be invisible to user and user should not know or see it.
- * @hide
- */
- @NonNull
- static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
- int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
- // Proxy method since ParsedActivityImpl is supposed to be package visibility
- return ParsedActivityImpl.makeAppDetailsActivity(packageName, processName, uiOptions,
- taskAffinity, hardwareAccelerated);
- }
-
int getColorMode();
int getConfigChanges();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java
index cf478b1..adb5f4f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java
index 1a5d110..5b623cd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttribution.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttribution.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.StringRes;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
index 5b6ecba..319ed7f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponent.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java
index c325d8d..76c5008 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentation.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentation.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java
index a7f7b00..fee0017 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfo.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
index b926d53..291ed0c 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponent.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermission.java
similarity index 95%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedPermission.java
index dc5347a..813d14d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermission.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermission.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java
index 64f4fbd..a5ba513 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroup.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroup.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
/** @hide */
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
index 608d08e..e5247f9 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcess.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java b/core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
index c66a5c1..ba5470f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/core/java/com/android/internal/pm/pkg/component/ParsedService.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedService.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedService.java
index 5fc251c..e361102 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
rename to core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java
index e17d1c4..984d50d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermission.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermission.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.internal.pm.pkg.component;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 686e1fc..9d0be4b 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -40,6 +40,7 @@
/**
* Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrayUtils {
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
@@ -48,35 +49,43 @@
private ArrayUtils() { /* cannot be instantiated */ }
+ @android.ravenwood.annotation.RavenwoodReplace
public static byte[] newUnpaddedByteArray(int minLen) {
return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static char[] newUnpaddedCharArray(int minLen) {
return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int[] newUnpaddedIntArray(int minLen) {
return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static boolean[] newUnpaddedBooleanArray(int minLen) {
return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static long[] newUnpaddedLongArray(int minLen) {
return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static float[] newUnpaddedFloatArray(int minLen) {
return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static Object[] newUnpaddedObjectArray(int minLen) {
return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SuppressWarnings("unchecked")
public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3496994..698c5ba 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4303,6 +4303,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is true.-->
<attr name="requireDeviceScreenOn" format="boolean"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4327,6 +4330,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is false.-->
<attr name="requireDeviceScreenOn"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 7642794..39d958c 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -205,6 +205,13 @@
<string name="config_satellite_emergency_handover_intent_action" translatable="false"></string>
<java-symbol type="string" name="config_satellite_emergency_handover_intent_action" />
+ <!-- Whether outgoing satellite datagrams should be sent to modem in demo mode. When satellite
+ is enabled for demo mode, if this config is enabled, outgoing datagrams will be sent to
+ modem; otherwise, success results will be returned. If demo mode is disabled, outgoing
+ datagrams are always sent to modem. -->
+ <bool name="config_send_satellite_datagram_to_modem_in_demo_mode">false</bool>
+ <java-symbol type="bool" name="config_send_satellite_datagram_to_modem_in_demo_mode" />
+
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
supported. The network will be torn down on the source transport, and will be
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 709646b..3a2e50a 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -155,7 +155,7 @@
<shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
<!-- Israel: 4 digits, known premium codes listed -->
- <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477" />
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
@@ -198,9 +198,6 @@
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
- <!-- Namibia: 5 digits -->
- <shortcode country="na" pattern="\\d{1,5}" free="40005" />
-
<!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 36e1223..4b02257 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -227,8 +227,7 @@
try {
// Send process level config change.
ClientTransaction transaction = newTransaction(activityThread);
- transaction.addCallback(ConfigurationChangeItem.obtain(
- new Configuration(newConfig), DEVICE_ID_INVALID));
+ transaction.addCallback(ConfigurationChangeItem.obtain(newConfig, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -245,7 +244,7 @@
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
transaction.addCallback(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), new Configuration(newConfig)));
+ activity.getActivityToken(), newConfig));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 4bbde0c..723c081 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import android.annotation.NonNull;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
@@ -80,74 +81,29 @@
@Test
public void testRecycleActivityConfigurationChangeItem() {
- ActivityConfigurationChangeItem emptyItem = ActivityConfigurationChangeItem.obtain(
- null /* activityToken */, Configuration.EMPTY);
- ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityConfigurationChangeItem item2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config()));
}
@Test
public void testRecycleActivityResultItem() {
- ActivityResultItem emptyItem = ActivityResultItem.obtain(
- null /* activityToken */, null /* resultInfoList */);
- ActivityResultItem item = ActivityResultItem.obtain(mActivityToken, resultInfoList());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityResultItem item2 = ActivityResultItem.obtain(mActivityToken, resultInfoList());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityResultItem.obtain(mActivityToken, resultInfoList()));
}
@Test
public void testRecycleConfigurationChangeItem() {
- ConfigurationChangeItem emptyItem = ConfigurationChangeItem.obtain(null, 0);
- ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config(), 1);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ConfigurationChangeItem item2 = ConfigurationChangeItem.obtain(config(), 1);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ConfigurationChangeItem.obtain(config(), 1));
}
@Test
public void testRecycleDestroyActivityItem() {
- DestroyActivityItem emptyItem = DestroyActivityItem.obtain(
- null /* activityToken */, false, 0);
- DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true, 117);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- DestroyActivityItem item2 = DestroyActivityItem.obtain(mActivityToken, true, 14);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true, 117));
}
@Test
public void testRecycleLaunchActivityItem() {
final IBinder activityToken = new Binder();
final Intent intent = new Intent("action");
- int ident = 57;
+ final int ident = 57;
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
activityInfo.setMaxAspectRatio(2.4f);
@@ -158,20 +114,19 @@
final Configuration overrideConfig = new Configuration();
overrideConfig.assetsSeq = 5;
final String referrer = "referrer";
- int procState = 4;
+ final int procState = 4;
final Bundle bundle = new Bundle();
bundle.putString("key", "value");
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
final IBinder assistToken = new Binder();
final IBinder shareableActivityToken = new Binder();
- int deviceId = 3;
+ final int deviceId = 3;
+ final IBinder taskFragmentToken = new Binder();
- final Supplier<LaunchActivityItem> itemSupplier = () -> new LaunchActivityItemBuilder()
- .setActivityToken(activityToken)
- .setIntent(intent)
+ testRecycle(() -> new LaunchActivityItemBuilder(
+ activityToken, intent, activityInfo)
.setIdent(ident)
- .setInfo(activityInfo)
.setCurConfig(config())
.setOverrideConfig(overrideConfig)
.setReferrer(referrer)
@@ -183,154 +138,74 @@
.setIsForward(true)
.setAssistToken(assistToken)
.setShareableActivityToken(shareableActivityToken)
- .setTaskFragmentToken(new Binder())
+ .setTaskFragmentToken(taskFragmentToken)
.setDeviceId(deviceId)
- .build();
-
- LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
- LaunchActivityItem item = itemSupplier.get();
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- LaunchActivityItem item2 = itemSupplier.get();
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ .build());
}
@Test
public void testRecycleActivityRelaunchItem() {
- ActivityRelaunchItem emptyItem = ActivityRelaunchItem.obtain(
- null /* activityToken */, null, null, 0, null, false);
- Configuration overrideConfig = new Configuration();
- overrideConfig.assetsSeq = 5;
- ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
- referrerIntentList(), 42, mergedConfig(), true);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ActivityRelaunchItem item2 = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
- referrerIntentList(), 42, mergedConfig(), true);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken,
+ resultInfoList(), referrerIntentList(), 42, mergedConfig(), true));
}
@Test
public void testRecycleMoveToDisplayItem() {
- MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(
- null /* activityToken */, 0, Configuration.EMPTY);
- MoveToDisplayItem item = MoveToDisplayItem.obtain(mActivityToken, 4, config());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- MoveToDisplayItem item2 = MoveToDisplayItem.obtain(mActivityToken, 3, config());
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> MoveToDisplayItem.obtain(mActivityToken, 4, config()));
}
@Test
public void testRecycleNewIntentItem() {
- NewIntentItem emptyItem = NewIntentItem.obtain(
- null /* activityToken */, null /* intents */, false /* resume */);
- NewIntentItem item = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- NewIntentItem item2 = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> NewIntentItem.obtain(mActivityToken, referrerIntentList(), false));
}
@Test
public void testRecyclePauseActivityItemItem() {
- PauseActivityItem emptyItem = PauseActivityItem.obtain(
- null /* activityToken */, false, false, 0, false, false);
- PauseActivityItem item = PauseActivityItem.obtain(
- mActivityToken, true, true, 5, true, true);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- PauseActivityItem item2 = PauseActivityItem.obtain(
- mActivityToken, true, false, 5, true, true);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, 5, true, true));
}
@Test
public void testRecycleResumeActivityItem() {
- ResumeActivityItem emptyItem = ResumeActivityItem.obtain(
- null /* activityToken */, false, false);
- ResumeActivityItem item = ResumeActivityItem.obtain(mActivityToken, 3, true, false);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- ResumeActivityItem item2 = ResumeActivityItem.obtain(mActivityToken, 2, true, false);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> ResumeActivityItem.obtain(mActivityToken, 3, true, false));
}
@Test
public void testRecycleStartActivityItem() {
- StartActivityItem emptyItem = StartActivityItem.obtain(
- null /* activityToken */, null /* activityOptions */);
- StartActivityItem item = StartActivityItem.obtain(mActivityToken,
- ActivityOptions.makeBasic());
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- StartActivityItem item2 = StartActivityItem.obtain(mActivityToken,
- ActivityOptions.makeBasic().setLaunchDisplayId(10));
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> StartActivityItem.obtain(mActivityToken, ActivityOptions.makeBasic()));
}
@Test
public void testRecycleStopItem() {
- StopActivityItem emptyItem = StopActivityItem.obtain(null /* activityToken */, 0);
- StopActivityItem item = StopActivityItem.obtain(mActivityToken, 4);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
-
- item.recycle();
- assertEquals(item, emptyItem);
-
- StopActivityItem item2 = StopActivityItem.obtain(mActivityToken, 3);
- assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+ testRecycle(() -> StopActivityItem.obtain(mActivityToken, 4));
}
@Test
public void testRecycleClientTransaction() {
- ClientTransaction emptyItem = ClientTransaction.obtain(null);
- ClientTransaction item = ClientTransaction.obtain(mApplicationThread);
- assertNotSame(item, emptyItem);
- assertNotEquals(item, emptyItem);
+ testRecycle(() -> ClientTransaction.obtain(mApplicationThread));
+ }
+ private void testRecycle(@NonNull Supplier<? extends ObjectPoolItem> obtain) {
+ // Reuse the same object after recycle.
+ final ObjectPoolItem item = obtain.get();
item.recycle();
- assertEquals(item, emptyItem);
+ final ObjectPoolItem item2 = obtain.get();
- ClientTransaction item2 = ClientTransaction.obtain(mApplicationThread);
assertSame(item, item2);
- assertNotEquals(item2, emptyItem);
+
+ // Create new object when the pool is empty.
+ final ObjectPoolItem item3 = obtain.get();
+
+ assertNotSame(item, item3);
+ assertEquals(item, item3);
+
+ // Reset fields after recycle.
+ item.recycle();
+
+ assertNotEquals(item, item3);
+
+ // Recycled objects are equal.
+ item3.recycle();
+
+ assertEquals(item, item3);
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 5a88bad..c0e2a49 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -18,6 +18,8 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -92,17 +94,18 @@
}
static class LaunchActivityItemBuilder {
- @Nullable
- private IBinder mActivityToken;
- @Nullable
- private Intent mIntent;
+ @NonNull
+ private final IBinder mActivityToken;
+ @NonNull
+ private final Intent mIntent;
+ @NonNull
+ private final ActivityInfo mInfo;
+ @NonNull
+ private final Configuration mCurConfig = new Configuration();
+ @NonNull
+ private final Configuration mOverrideConfig = new Configuration();
+
private int mIdent;
- @Nullable
- private ActivityInfo mInfo;
- @Nullable
- private Configuration mCurConfig;
- @Nullable
- private Configuration mOverrideConfig;
private int mDeviceId;
@Nullable
private String mReferrer;
@@ -130,16 +133,11 @@
@Nullable
private IBinder mTaskFragmentToken;
- @NonNull
- LaunchActivityItemBuilder setActivityToken(@Nullable IBinder activityToken) {
- mActivityToken = activityToken;
- return this;
- }
-
- @NonNull
- LaunchActivityItemBuilder setIntent(@Nullable Intent intent) {
- mIntent = intent;
- return this;
+ LaunchActivityItemBuilder(@NonNull IBinder activityToken, @NonNull Intent intent,
+ @NonNull ActivityInfo info) {
+ mActivityToken = requireNonNull(activityToken);
+ mIntent = requireNonNull(intent);
+ mInfo = requireNonNull(info);
}
@NonNull
@@ -149,20 +147,14 @@
}
@NonNull
- LaunchActivityItemBuilder setInfo(@Nullable ActivityInfo info) {
- mInfo = info;
+ LaunchActivityItemBuilder setCurConfig(@NonNull Configuration curConfig) {
+ mCurConfig.setTo(curConfig);
return this;
}
@NonNull
- LaunchActivityItemBuilder setCurConfig(@Nullable Configuration curConfig) {
- mCurConfig = curConfig;
- return this;
- }
-
- @NonNull
- LaunchActivityItemBuilder setOverrideConfig(@Nullable Configuration overrideConfig) {
- mOverrideConfig = overrideConfig;
+ LaunchActivityItemBuilder setOverrideConfig(@NonNull Configuration overrideConfig) {
+ mOverrideConfig.setTo(overrideConfig);
return this;
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index f2b0f2e..443dcb4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -44,6 +44,8 @@
import android.app.ClientTransactionHandler;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -290,7 +292,7 @@
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
+ spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
launchTransaction.addCallback(launchItem);
mExecutor.execute(launchTransaction);
@@ -322,7 +324,7 @@
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder().setActivityToken(token).build());
+ spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
launchTransaction.addTransactionItem(launchItem);
mExecutor.execute(launchTransaction);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 4aa62c5..07921bf 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -173,11 +173,9 @@
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
- final LaunchActivityItem item = new LaunchActivityItemBuilder()
- .setActivityToken(activityToken)
- .setIntent(intent)
+ final LaunchActivityItem item = new LaunchActivityItemBuilder(
+ activityToken, intent, activityInfo)
.setIdent(ident)
- .setInfo(activityInfo)
.setCurConfig(config())
.setOverrideConfig(overrideConfig)
.setReferrer(referrer)
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
index c00eb91..4d45daf 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
@@ -52,16 +52,18 @@
private PendingTransactionActions mPendingActions;
@Mock
private IWindow mWindow;
- @Mock
- private ClientWindowFrames mFrames;
- @Mock
- private MergedConfiguration mConfiguration;
- @Mock
+
private InsetsState mInsetsState;
+ private ClientWindowFrames mFrames;
+ private MergedConfiguration mConfiguration;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+
+ mInsetsState = new InsetsState();
+ mFrames = new ClientWindowFrames();
+ mConfiguration = new MergedConfiguration();
}
@Test
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 3e3c77b..03c38cc 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -25,9 +25,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.annotation.EnforcePermission;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.test.FakePermissionEnforcer;
import androidx.test.filters.SmallTest;
@@ -57,7 +59,8 @@
@Before
public void setUp() {
- mService = new TestDeviceStateManagerService();
+ FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+ mService = new TestDeviceStateManagerService(permissionEnforcer);
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
assertFalse(mService.mCallbacks.isEmpty());
}
@@ -261,6 +264,10 @@
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
+ TestDeviceStateManagerService(FakePermissionEnforcer enforcer) {
+ super(enforcer);
+ }
+
private DeviceStateInfo getInfo() {
final int mergedBaseState = mBaseStateRequest == null
? mBaseState : mBaseStateRequest.state;
@@ -380,7 +387,10 @@
// No-op in the test since DeviceStateManagerGlobal just calls into the system server with
// no business logic around it.
@Override
- public void onStateRequestOverlayDismissed(boolean shouldCancelMode) {}
+ @EnforcePermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void onStateRequestOverlayDismissed(boolean shouldCancelMode) {
+ onStateRequestOverlayDismissed_enforcePermission();
+ }
public void setSupportedStates(int[] states) {
mSupportedStates = states;
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index 66fabec..a4bce9e 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -23,6 +23,8 @@
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.Buffer;
import java.nio.ShortBuffer;
@@ -43,6 +45,7 @@
* Determines how the mesh is represented and will be drawn.
*/
@IntDef({TRIANGLES, TRIANGLE_STRIP})
+ @Retention(RetentionPolicy.SOURCE)
private @interface Mode {}
/**
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 5509f00..45e29a8 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -62,6 +62,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Objects;
@@ -116,6 +118,7 @@
*/
@IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP,
TYPE_URI_ADAPTIVE_BITMAP})
+ @Retention(RetentionPolicy.SOURCE)
public @interface IconType {
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index b26d061..07c5429 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.unfold;
+import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
@@ -188,23 +189,27 @@
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull TransitionFinishCallback finishCallback) {
- if (info.getType() == TRANSIT_CHANGE) {
- // TODO (b/286928742) unfold transition handler should be part of mixed handler to
- // handle merges better.
- for (int i = 0; i < info.getChanges().size(); ++i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null
- && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
- // Tasks that are always on top (e.g. bubbles), will handle their own transition
- // as they are on top of everything else. So skip merging transitions here.
- return;
- }
- }
- // Apply changes happening during the unfold animation immediately
- t.apply();
- finishCallback.onTransitionFinished(null);
+ if (info.getType() != TRANSIT_CHANGE) {
+ return;
}
+ if ((info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
+ return;
+ }
+ // TODO (b/286928742) unfold transition handler should be part of mixed handler to
+ // handle merges better.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
+ // Tasks that are always on top (e.g. bubbles), will handle their own transition
+ // as they are on top of everything else. So skip merging transitions here.
+ return;
+ }
+ }
+ // Apply changes happening during the unfold animation immediately
+ t.apply();
+ finishCallback.onTransitionFinished(null);
}
/** Whether `request` contains an unfold action. */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 7917ba6..6d73c12 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.unfold;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +47,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.TransitionInfoBuilder;
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
@@ -265,6 +267,42 @@
verify(finishCallback).onTransitionFinished(any());
}
+ @Test
+ public void mergeAnimation_eatsDisplayOnlyTransitions() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+ TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+ TransitionFinishCallback mergeCallback = mock(TransitionFinishCallback.class);
+
+ mUnfoldTransitionHandler.startAnimation(
+ mTransition,
+ mock(TransitionInfo.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ finishCallback);
+
+ // Offer a keyguard unlock transition - this should NOT merge
+ mUnfoldTransitionHandler.mergeAnimation(
+ new Binder(),
+ new TransitionInfoBuilder(TRANSIT_CHANGE, TRANSIT_FLAG_KEYGUARD_GOING_AWAY).build(),
+ mock(SurfaceControl.Transaction.class),
+ mTransition,
+ mergeCallback);
+ verify(finishCallback, never()).onTransitionFinished(any());
+
+ // Offer a CHANGE-only transition - this SHOULD merge (b/278064943)
+ mUnfoldTransitionHandler.mergeAnimation(
+ new Binder(),
+ new TransitionInfoBuilder(TRANSIT_CHANGE).build(),
+ mock(SurfaceControl.Transaction.class),
+ mTransition,
+ mergeCallback);
+ verify(mergeCallback).onTransitionFinished(any());
+
+ // We should never have finished the original transition.
+ verify(finishCallback, never()).onTransitionFinished(any());
+ }
+
private TransitionRequestInfo createUnfoldTransitionRequestInfo() {
ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
@@ -372,4 +410,4 @@
private TransitionInfo createNonUnfoldTransitionInfo() {
return new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
}
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index a1b05c1..a7d6423 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -597,7 +597,13 @@
SkIRect clipBounds;
if (enableClip) {
uirenderer::Rect initialClipBounds;
- props.getClippingRectForFlags(props.getClippingFlags(), &initialClipBounds);
+ const auto clipFlags = props.getClippingFlags();
+ if (clipFlags) {
+ props.getClippingRectForFlags(clipFlags, &initialClipBounds);
+ } else {
+ // Works for RenderNode::damageSelf()
+ initialClipBounds.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ }
clipBounds =
info.damageAccumulator
->computeClipAndTransform(initialClipBounds.toSkRect(), &transform)
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e706eb0..d55d28d 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -527,52 +527,65 @@
return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge);
}
-struct DestroySemaphoreInfo {
+class SharedSemaphoreInfo : public LightRefBase<SharedSemaphoreInfo> {
PFN_vkDestroySemaphore mDestroyFunction;
VkDevice mDevice;
VkSemaphore mSemaphore;
+ GrBackendSemaphore mGrBackendSemaphore;
- DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
- VkSemaphore semaphore)
- : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {}
+ SharedSemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
+ VkSemaphore semaphore)
+ : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {
+ mGrBackendSemaphore.initVulkan(semaphore);
+ }
- ~DestroySemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+ ~SharedSemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+
+ friend class LightRefBase<SharedSemaphoreInfo>;
+ friend class sp<SharedSemaphoreInfo>;
+
+public:
+ VkSemaphore semaphore() const { return mSemaphore; }
+
+ GrBackendSemaphore* grBackendSemaphore() { return &mGrBackendSemaphore; }
};
static void destroy_semaphore(void* context) {
- DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context);
- delete info;
+ SharedSemaphoreInfo* info = reinterpret_cast<SharedSemaphoreInfo*>(context);
+ info->decStrong(0);
}
VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ATRACE_NAME("Vulkan finish frame");
- VkExportSemaphoreCreateInfo exportInfo;
- exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
- exportInfo.pNext = nullptr;
- exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkSemaphoreCreateInfo semaphoreInfo;
- semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreInfo.pNext = &exportInfo;
- semaphoreInfo.flags = 0;
- VkSemaphore semaphore;
- VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
- ALOGE_IF(VK_SUCCESS != err, "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
-
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
-
+ sp<SharedSemaphoreInfo> sharedSemaphore;
GrFlushInfo flushInfo;
- if (err == VK_SUCCESS) {
- flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
- flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext =
- new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
- } else {
- semaphore = VK_NULL_HANDLE;
+
+ {
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ ALOGE_IF(VK_SUCCESS != err,
+ "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
+
+ if (err == VK_SUCCESS) {
+ sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
+ flushInfo.fNumSemaphores = 1;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
+ flushInfo.fFinishedProc = destroy_semaphore;
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
+ }
}
+
GrDirectContext* context = GrAsDirectContext(surface->recordingContext());
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
@@ -581,37 +594,34 @@
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
- if (semaphore != VK_NULL_HANDLE) {
- if (submitted == GrSemaphoresSubmitted::kYes) {
- if (mFrameBoundaryANDROID) {
- // retrieve VkImage used as render target
- VkImage image = VK_NULL_HANDLE;
- GrBackendRenderTarget backendRenderTarget =
- SkSurfaces::GetBackendRenderTarget(
- surface, SkSurfaces::BackendHandleAccess::kFlushRead);
- if (backendRenderTarget.isValid()) {
- GrVkImageInfo info;
- if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
- image = info.fImage;
- } else {
- ALOGE("Frame boundary: backend is not vulkan");
- }
+ if (sharedSemaphore) {
+ if (submitted == GrSemaphoresSubmitted::kYes && mFrameBoundaryANDROID) {
+ // retrieve VkImage used as render target
+ VkImage image = VK_NULL_HANDLE;
+ GrBackendRenderTarget backendRenderTarget = SkSurfaces::GetBackendRenderTarget(
+ surface, SkSurfaces::BackendHandleAccess::kFlushRead);
+ if (backendRenderTarget.isValid()) {
+ GrVkImageInfo info;
+ if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
+ image = info.fImage;
} else {
- ALOGE("Frame boundary: invalid backend render target");
+ ALOGE("Frame boundary: backend is not vulkan");
}
- // frameBoundaryANDROID needs to know about mSwapSemaphore, but
- // it won't wait on it.
- mFrameBoundaryANDROID(mDevice, semaphore, image);
+ } else {
+ ALOGE("Frame boundary: invalid backend render target");
}
+ // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+ // it won't wait on it.
+ mFrameBoundaryANDROID(mDevice, sharedSemaphore->semaphore(), image);
}
VkSemaphoreGetFdInfoKHR getFdInfo;
getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = semaphore;
+ getFdInfo.semaphore = sharedSemaphore->semaphore();
getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
int fenceFd = -1;
- err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
drawResult.presentFence.reset(fenceFd);
} else {
@@ -732,15 +742,15 @@
return INVALID_OPERATION;
}
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
+ auto sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
// Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback
GrFlushInfo flushInfo;
flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
grContext->submit();
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 985a758..0f48abeb 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -54,6 +56,7 @@
flag = false,
prefix = "AUDIO_HAL_TYPE_",
value = {AUDIO_HAL_TYPE_HIDL, AUDIO_HAL_TYPE_AIDL})
+ @Retention(RetentionPolicy.SOURCE)
public @interface AudioHalType {}
/** AudioHalVersionInfo object of all valid Audio HAL versions. */
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index faa7f7f..5331046 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -2018,6 +2018,8 @@
* {@link #HDCP_V2_1},
* {@link #HDCP_V2_2},
* {@link #HDCP_V2_3}
+ *
+ * @removed mistakenly exposed previously
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@@ -2121,6 +2123,8 @@
* {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
* {@link #SECURITY_LEVEL_HW_SECURE_DECODE},
* {@link #SECURITY_LEVEL_HW_SECURE_ALL}
+ *
+ * @removed mistakenly exposed previously
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 74ca4b8..99fcaf2 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -267,20 +267,26 @@
HEAD_TRACKING_MODE_DISABLED,
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingMode {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingMode {};
/** @hide */
@IntDef(flag = false, value = {
HEAD_TRACKING_MODE_DISABLED,
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingModeSet {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingModeSet {};
/** @hide */
@IntDef(flag = false, value = {
HEAD_TRACKING_MODE_RELATIVE_WORLD,
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
- }) public @interface HeadTrackingModeSupported {};
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HeadTrackingModeSupported {};
/**
* @hide
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/media/java/android/media/tv/ad/ITvAdManager.aidl
similarity index 61%
copy from services/core/java/com/android/server/pm/pkg/component/ParsedService.java
copy to media/java/android/media/tv/ad/ITvAdManager.aidl
index 5fc251c..92cc923 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/media/java/android/media/tv/ad/ITvAdManager.aidl
@@ -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,16 +14,12 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package android.media.tv.ad;
-import android.annotation.Nullable;
-
-/** @hide **/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface ParsedService extends ParsedMainComponent {
-
- int getForegroundServiceType();
-
- @Nullable
- String getPermission();
+/**
+ * Interface to the TV AD service.
+ * @hide
+ */
+interface ITvAdManager {
+ void startAdService(in IBinder sessionToken, int userId);
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/media/java/android/media/tv/ad/ITvAdSession.aidl
similarity index 61%
copy from services/core/java/com/android/server/pm/pkg/component/ParsedService.java
copy to media/java/android/media/tv/ad/ITvAdSession.aidl
index 5fc251c..b834f1b9 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/media/java/android/media/tv/ad/ITvAdSession.aidl
@@ -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,16 +14,12 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package android.media.tv.ad;
-import android.annotation.Nullable;
-
-/** @hide **/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface ParsedService extends ParsedMainComponent {
-
- int getForegroundServiceType();
-
- @Nullable
- String getPermission();
+/**
+ * Sub-interface of ITvAdService which is created per session and has its own context.
+ * @hide
+ */
+oneway interface ITvAdSession {
+ void startAdService();
}
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
new file mode 100644
index 0000000..aa5a290
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.media.tv.ad;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Central system API to the overall client-side TV AD architecture, which arbitrates interaction
+ * between applications and AD services.
+ * @hide
+ */
+public class TvAdManager {
+ private static final String TAG = "TvAdManager";
+
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ public TvAdManager(ITvAdManager service, int userId) {
+ mService = service;
+ mUserId = userId;
+ }
+
+ /**
+ * The Session provides the per-session functionality of AD service.
+ */
+ public static final class Session {
+ private final IBinder mToken;
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ private Session(IBinder token, ITvAdManager service, int userId) {
+ mToken = token;
+ mService = service;
+ mUserId = userId;
+ }
+
+ void startAdService() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.startAdService(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java
new file mode 100644
index 0000000..61101f0
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdService.java
@@ -0,0 +1,60 @@
+/*
+ * 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.media.tv.ad;
+
+import android.app.Service;
+import android.view.KeyEvent;
+
+/**
+ * The TvAdService class represents a TV client-side advertisement service.
+ * @hide
+ */
+public abstract class TvAdService extends Service {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvAdService";
+
+ /**
+ * Base class for derived classes to implement to provide a TV AD session.
+ */
+ public abstract static class Session implements KeyEvent.Callback {
+ /**
+ * Starts TvAdService session.
+ */
+ public void onStartAdService() {
+ }
+
+ void startAdService() {
+ onStartAdService();
+ }
+ }
+
+ /**
+ * Implements the internal ITvAdService interface.
+ */
+ public static class ITvAdSessionWrapper extends ITvAdSession.Stub {
+ private final Session mSessionImpl;
+
+ public ITvAdSessionWrapper(Session mSessionImpl) {
+ this.mSessionImpl = mSessionImpl;
+ }
+
+ @Override
+ public void startAdService() {
+ mSessionImpl.startAdService();
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
new file mode 100644
index 0000000..1a3771a
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -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 android.media.tv.ad;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.ViewGroup;
+
+/**
+ * Displays contents of TV AD services.
+ * @hide
+ */
+public class TvAdView extends ViewGroup {
+ private static final String TAG = "TvAdView";
+ private static final boolean DEBUG = false;
+
+ // TODO: create session
+ private TvAdManager.Session mSession;
+
+ public TvAdView(Context context) {
+ super(context, /* attrs = */null, /* defStyleAttr = */0);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onLayout (left=" + l + ", top=" + t + ", right=" + r + ", bottom=" + b + ",)");
+ }
+ }
+
+ /**
+ * Starts the AD service.
+ */
+ public void startAdService() {
+ if (DEBUG) {
+ Log.d(TAG, "start");
+ }
+ if (mSession != null) {
+ mSession.startAdService();
+ }
+ }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch.xml
deleted file mode 100644
index 1ffdad4..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch.xml
+++ /dev/null
@@ -1,25 +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.
- -->
-
-<Switch
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/switch_widget"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:clickable="false"
- android:focusable="false"
- android:theme="@style/Switch.SettingsLib" />
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 1054e00..e3f8fbb 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -47,7 +47,14 @@
android:textAppearance="?android:attr/textAppearanceListItem"
style="@style/MainSwitchText.Settingslib" />
- <include layout="@layout/settingslib_main_switch" />
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 4a0e7b3..255b2c9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -49,7 +49,14 @@
android:lineBreakWordStyle="phrase"
style="@style/MainSwitchText.Settingslib" />
- <include layout="@layout/settingslib_main_switch" />
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch.xml
deleted file mode 100644
index 12c1d76..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch.xml
+++ /dev/null
@@ -1,25 +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.
- -->
-
-<Switch
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/switch_widget"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:focusable="false"
- android:theme="@style/SwitchBar.Switch.Settingslib" />
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index fe64fea..bf34db9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -38,6 +38,13 @@
android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
android:textAlignment="viewStart"/>
- <include layout="@layout/settingslib_main_switch" />
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/SwitchBar.Switch.Settingslib"/>
</LinearLayout>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
new file mode 100644
index 0000000..d0d2dc0
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.settingslib.spaprivileged.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * A [BroadcastReceiver] flow for the given [intentFilter].
+ */
+fun Context.broadcastReceiverFlow(intentFilter: IntentFilter): Flow<Intent> = callbackFlow {
+ val broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ trySend(intent)
+ }
+ }
+ registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
+
+ awaitClose { unregisterReceiver(broadcastReceiver) }
+}.conflate().flowOn(Dispatchers.Default)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
new file mode 100644
index 0000000..dfaf3c6
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.settingslib.spaprivileged.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class BroadcastReceiverFlowTest {
+
+ private var registeredBroadcastReceiver: BroadcastReceiver? = null
+
+ private val context = mock<Context> {
+ on {
+ registerReceiver(any(), eq(INTENT_FILTER), eq(Context.RECEIVER_NOT_EXPORTED))
+ } doAnswer {
+ registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
+ null
+ }
+ }
+
+ @Test
+ fun broadcastReceiverFlow_registered() = runBlocking {
+ val flow = context.broadcastReceiverFlow(INTENT_FILTER)
+
+ flow.firstWithTimeoutOrNull()
+
+ assertThat(registeredBroadcastReceiver).isNotNull()
+ }
+
+ @Test
+ fun broadcastReceiverFlow_isCalledOnReceive() = runBlocking {
+ var onReceiveIsCalled = false
+ launch {
+ context.broadcastReceiverFlow(INTENT_FILTER).first {
+ onReceiveIsCalled = true
+ true
+ }
+ }
+
+ delay(100)
+ registeredBroadcastReceiver!!.onReceive(context, Intent())
+ delay(100)
+
+ assertThat(onReceiveIsCalled).isTrue()
+ }
+
+ private companion object {
+ val INTENT_FILTER = IntentFilter()
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e9f4b5c..245fe6e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1310,7 +1310,7 @@
// Set default string with battery level in device connected situation.
if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
stringRes = R.string.bluetooth_battery_level_untethered;
- } else if (batteryLevelPercentageString != null) {
+ } else if (batteryLevelPercentageString != null && !shortSummary) {
stringRes = R.string.bluetooth_battery_level;
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 7e8fe7e..d9fe733 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -445,5 +445,6 @@
VALIDATORS.put(Global.Wearable.PHONE_SWITCHING_SUPPORTED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.CONNECTIVITY_KEEP_DATA_ON, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2e174e2..c0d83c4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -686,7 +686,8 @@
Settings.Global.Wearable.PHONE_SWITCHING_SUPPORTED,
Settings.Global.Wearable.WEAR_MEDIA_CONTROLS_PACKAGE,
Settings.Global.Wearable.WEAR_MEDIA_SESSIONS_PACKAGE,
- Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED);
+ Settings.Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED,
+ Settings.Global.Wearable.CONNECTIVITY_KEEP_DATA_ON);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 0e9f8b1..80fd516 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -294,8 +294,6 @@
"tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
// Keyguard helper
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index aebdaab..e340209 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -107,8 +107,16 @@
}
flag {
- name: "qs_new_pipeline"
- namespace: "systemui"
- description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
- bug: "241772429"
+ name: "qs_new_pipeline"
+ namespace: "systemui"
+ description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
+ bug: "241772429"
}
+
+flag {
+ name: "coroutine_tracing"
+ namespace: "systemui"
+ description: "Adds thread-local data to System UI's global coroutine scopes to "
+ "allow for tracing of coroutine continuations using System UI's tracinglib"
+ bug: "289353932"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 3928767..7eb7dac 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -208,12 +208,8 @@
val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
val isSplitAroundTheFold =
foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
- val currentSceneKey by
- remember(isSplitAroundTheFold) {
- mutableStateOf(
- if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
- )
- }
+ val currentSceneKey =
+ if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
SceneTransitionLayout(
currentScene = currentSceneKey,
@@ -405,8 +401,7 @@
if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
PatternBouncer(
viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
+ modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
)
}
else -> Unit
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index fb50f69..243751fa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -76,6 +76,8 @@
val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+ val isDigitButtonAnimationEnabled: Boolean by
+ viewModel.isDigitButtonAnimationEnabled.collectAsState()
val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
LaunchedEffect(animateFailure) {
@@ -94,10 +96,11 @@
) {
repeat(9) { index ->
DigitButton(
- index + 1,
- isInputEnabled,
- viewModel::onPinButtonClicked,
- buttonScaleAnimatables[index]::value,
+ digit = index + 1,
+ isInputEnabled = isInputEnabled,
+ onClicked = viewModel::onPinButtonClicked,
+ scaling = buttonScaleAnimatables[index]::value,
+ isAnimationEnabled = isDigitButtonAnimationEnabled,
)
}
@@ -116,10 +119,11 @@
)
DigitButton(
- 0,
- isInputEnabled,
- viewModel::onPinButtonClicked,
- buttonScaleAnimatables[10]::value,
+ digit = 0,
+ isInputEnabled = isInputEnabled,
+ onClicked = viewModel::onPinButtonClicked,
+ scaling = buttonScaleAnimatables[10]::value,
+ isAnimationEnabled = isDigitButtonAnimationEnabled,
)
ActionButton(
@@ -143,15 +147,17 @@
isInputEnabled: Boolean,
onClicked: (Int) -> Unit,
scaling: () -> Float,
+ isAnimationEnabled: Boolean,
) {
PinPadButton(
onClicked = { onClicked(digit) },
isEnabled = isInputEnabled,
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ isAnimationEnabled = isAnimationEnabled,
modifier =
Modifier.graphicsLayer {
- val scale = scaling()
+ val scale = if (isAnimationEnabled) scaling() else 1f
scaleX = scale
scaleY = scale
}
@@ -195,6 +201,7 @@
isEnabled = isInputEnabled && !isHidden,
backgroundColor = backgroundColor,
foregroundColor = foregroundColor,
+ isAnimationEnabled = true,
modifier =
Modifier.graphicsLayer {
alpha = hiddenAlpha
@@ -216,6 +223,7 @@
isEnabled: Boolean,
backgroundColor: Color,
foregroundColor: Color,
+ isAnimationEnabled: Boolean,
modifier: Modifier = Modifier,
onLongPressed: (() -> Unit)? = null,
content: @Composable (contentColor: () -> Color) -> Unit,
@@ -243,7 +251,7 @@
val cornerRadius: Dp by
animateDpAsState(
- if (isPressed) 24.dp else pinButtonSize / 2,
+ if (isAnimationEnabled && isPressed) 24.dp else pinButtonSize / 2,
label = "PinButton round corners",
animationSpec = tween(animDurationMillis, easing = animEasing)
)
@@ -251,7 +259,7 @@
val containerColor: Color by
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.primary
+ isAnimationEnabled && isPressed -> MaterialTheme.colorScheme.primary
else -> backgroundColor
},
label = "Pin button container color",
@@ -260,7 +268,7 @@
val contentColor =
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.onPrimary
+ isAnimationEnabled && isPressed -> MaterialTheme.colorScheme.onPrimary
else -> foregroundColor
},
label = "Pin button container color",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 3780468..09706be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -12,7 +12,6 @@
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -25,10 +24,12 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.transitions
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -76,17 +77,24 @@
currentScene = currentScene,
onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) },
transitions = sceneTransitions,
+ edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize)
) {
scene(
TransitionSceneKey.Blank,
- userActions = mapOf(Swipe.Left to TransitionSceneKey.Communal)
+ userActions =
+ mapOf(
+ Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal
+ )
) {
BlankScene { showSceneTransitionLayout = false }
}
scene(
TransitionSceneKey.Communal,
- userActions = mapOf(Swipe.Right to TransitionSceneKey.Blank),
+ userActions =
+ mapOf(
+ Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank
+ ),
) {
CommunalScene(viewModel, modifier = modifier)
}
@@ -105,14 +113,12 @@
Box(modifier.fillMaxSize()) {
Column(
Modifier.fillMaxHeight()
- .width(100.dp)
+ .width(ContainerDimensions.EdgeSwipeSize)
.align(Alignment.CenterEnd)
.background(Color(0x55e9f2eb)),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text("Default scene")
-
IconButton(onClick = hideSceneTransitionLayout) {
Icon(Icons.Filled.Close, contentDescription = "Close button")
}
@@ -142,3 +148,7 @@
fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
return this.identity as CommunalSceneKey
}
+
+object ContainerDimensions {
+ val EdgeSwipeSize = 40.dp
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1429782..6e18cb9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -22,10 +22,11 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
@@ -42,6 +43,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -65,6 +67,7 @@
LazyHorizontalGrid(
modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
rows = GridCells.Fixed(CommunalContentSize.FULL.span),
+ contentPadding = PaddingValues(horizontal = Dimensions.Spacing),
horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
) {
@@ -92,6 +95,16 @@
LocalContext.current.getString(R.string.button_to_open_widget_picker)
)
}
+
+ // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
+ // touches, so that the SceneTransitionLayout can intercept the touches and allow an edge
+ // swipe back to the blank scene.
+ Spacer(
+ Modifier.height(Dimensions.GridHeight)
+ .align(Alignment.CenterStart)
+ .width(Dimensions.Spacing)
+ .pointerInput(Unit) {}
+ )
}
}
@@ -164,11 +177,7 @@
@Composable
private fun Umo(viewModel: CommunalViewModel, modifier: Modifier = Modifier) {
AndroidView(
- modifier =
- modifier
- .width(Dimensions.CardWidth)
- .height(Dimensions.CardHeightThird)
- .padding(Dimensions.Spacing),
+ modifier = modifier,
factory = {
viewModel.mediaHost.expansion = MediaHostState.EXPANDED
viewModel.mediaHost.showsOnlyActiveMedia = false
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index b77a60b..6153e19 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -18,7 +18,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
@@ -36,7 +35,6 @@
import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.graphics.drawscope.scale
-import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.IntermediateMeasureScope
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.Placeable
@@ -47,7 +45,6 @@
import androidx.compose.ui.unit.round
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.SharedElementTransformation
-import com.android.compose.modifiers.thenIf
import com.android.compose.ui.util.lerp
/** An element on screen, that can be composed in one or more scenes. */
@@ -146,8 +143,6 @@
element
}
- val lastSharedValues = element.lastSharedValues
- val lastSceneValues = sceneValues.lastValues
DisposableEffect(scene, sceneValues, element) {
onDispose {
@@ -160,18 +155,6 @@
}
}
- val alpha =
- remember(layoutImpl, element, scene, sceneValues) {
- derivedStateOf { elementAlpha(layoutImpl, element, scene, sceneValues) }
- }
- val isOpaque by remember(alpha) { derivedStateOf { alpha.value == 1f } }
- SideEffect {
- if (isOpaque) {
- lastSharedValues.alpha = 1f
- lastSceneValues.alpha = 1f
- }
- }
-
val drawScale by
remember(layoutImpl, element, scene, sceneValues) {
derivedStateOf { getDrawScale(layoutImpl, element, scene, sceneValues) }
@@ -200,14 +183,6 @@
place(layoutImpl, scene, element, sceneValues, placeable, placementScope = this)
}
}
- .thenIf(!isOpaque) {
- Modifier.graphicsLayer {
- val alpha = alpha.value
- this.alpha = alpha
- lastSharedValues.alpha = alpha
- lastSceneValues.alpha = alpha
- }
- }
.testTag(key.testTag)
}
@@ -325,6 +300,61 @@
}
}
+/**
+ * Whether the element is opaque or not.
+ *
+ * Important: The logic here should closely match the logic in [elementAlpha]. Note that we don't
+ * reuse [elementAlpha] and simply check if alpha == 1f because [isElementOpaque] is checked during
+ * placement and we don't want to read the transition progress in that phase.
+ */
+private fun isElementOpaque(
+ layoutImpl: SceneTransitionLayoutImpl,
+ element: Element,
+ scene: Scene,
+ sceneValues: Element.TargetValues,
+): Boolean {
+ val state = layoutImpl.state.transitionState
+
+ if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+ return true
+ }
+
+ if (!layoutImpl.isTransitionReady(state)) {
+ val lastValue =
+ sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified }
+ ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f
+
+ return lastValue == 1f
+ }
+
+ val fromScene = state.fromScene
+ val toScene = state.toScene
+ val fromValues = element.sceneValues[fromScene]
+ val toValues = element.sceneValues[toScene]
+
+ if (fromValues == null && toValues == null) {
+ error("This should not happen, element $element is neither in $fromScene or $toScene")
+ }
+
+ val isSharedElement = fromValues != null && toValues != null
+ if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
+ return true
+ }
+
+ return layoutImpl.transitions
+ .transitionSpec(fromScene, toScene)
+ .transformations(element.key, scene.key)
+ .alpha == null
+}
+
+/**
+ * Whether the element is opaque or not.
+ *
+ * Important: The logic here should closely match the logic in [isElementOpaque]. Note that we don't
+ * reuse [elementAlpha] in [isElementOpaque] and simply check if alpha == 1f because
+ * [isElementOpaque] is checked during placement and we don't want to read the transition progress
+ * in that phase.
+ */
private fun elementAlpha(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
@@ -446,6 +476,8 @@
}
val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
+ val lastSharedValues = element.lastSharedValues
+ val lastValues = sceneValues.lastValues
val targetOffset =
computeValue(
layoutImpl,
@@ -456,16 +488,31 @@
idleValue = targetOffsetInScene,
currentValue = { currentOffset },
lastValue = {
- sceneValues.lastValues.offset.takeIf { it.isSpecified }
- ?: element.lastSharedValues.offset.takeIf { it.isSpecified }
- ?: currentOffset
+ lastValues.offset.takeIf { it.isSpecified }
+ ?: lastSharedValues.offset.takeIf { it.isSpecified } ?: currentOffset
},
::lerp,
)
- element.lastSharedValues.offset = targetOffset
- sceneValues.lastValues.offset = targetOffset
- placeable.place((targetOffset - currentOffset).round())
+ lastSharedValues.offset = targetOffset
+ lastValues.offset = targetOffset
+
+ val offset = (targetOffset - currentOffset).round()
+ if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
+ // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
+ // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
+ // case.
+ placeable.place(offset)
+ lastSharedValues.alpha = 1f
+ lastValues.alpha = 1f
+ } else {
+ placeable.placeWithLayer(offset) {
+ val alpha = elementAlpha(layoutImpl, element, scene, sceneValues)
+ this.alpha = alpha
+ lastSharedValues.alpha = alpha
+ lastValues.alpha = alpha
+ }
+ }
}
}
@@ -527,21 +574,17 @@
error("This should not happen, element $element is neither in $fromScene or $toScene")
}
- // TODO(b/291053278): Handle overscroll correctly. We should probably coerce between [0f, 1f]
- // here and consume overflows at drawing time, somehow reusing Compose OverflowEffect or some
- // similar mechanism.
- val transitionProgress = state.progress
-
// The element is shared: interpolate between the value in fromScene and the value in toScene.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
val isSharedElement = fromValues != null && toValues != null
if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
- return lerp(
- sceneValue(fromValues!!),
- sceneValue(toValues!!),
- transitionProgress,
- )
+ val start = sceneValue(fromValues!!)
+ val end = sceneValue(toValues!!)
+
+ // Make sure we don't read progress if values are the same and we don't need to interpolate,
+ // so we don't invalidate the phase where this is read.
+ return if (start == end) start else lerp(start, end, state.progress)
}
val transformation =
@@ -576,8 +619,15 @@
idleValue,
)
+ // Make sure we don't read progress if values are the same and we don't need to interpolate, so
+ // we don't invalidate the phase where this is read.
+ if (targetValue == idleValue) {
+ return targetValue
+ }
+
+ val progress = state.progress
// TODO(b/290184746): Make sure that we don't overflow transformations associated to a range.
- val rangeProgress = transformation.range?.progress(transitionProgress) ?: transitionProgress
+ val rangeProgress = transformation.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the value before entering/after leaving.
val isEntering = scene.key == toScene
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 9d71801..838cb3b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -39,13 +39,13 @@
@VisibleForTesting
class SceneGestureHandler(
- private val layoutImpl: SceneTransitionLayoutImpl,
+ internal val layoutImpl: SceneTransitionLayoutImpl,
internal val orientation: Orientation,
private val coroutineScope: CoroutineScope,
) {
val draggable: DraggableHandler = SceneDraggableHandler(this)
- private var transitionState
+ internal var transitionState
get() = layoutImpl.state.transitionState
set(value) {
layoutImpl.state.transitionState = value
@@ -58,7 +58,7 @@
* Note: the initialScene here does not matter, it's only used for initializing the transition
* and will be replaced when a drag event starts.
*/
- private val swipeTransition = SwipeTransition(initialScene = currentScene)
+ internal val swipeTransition = SwipeTransition(initialScene = currentScene)
internal val currentScene: Scene
get() = layoutImpl.scene(transitionState.currentScene)
@@ -415,7 +415,7 @@
}
}
- private class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
+ internal class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
var _currentScene by mutableStateOf(initialScene)
override val currentScene: SceneKey
get() = _currentScene.key
@@ -598,9 +598,29 @@
return PriorityNestedScrollConnection(
canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
canChangeScene = offsetBeforeStart == Offset.Zero
- gestureHandler.isDrivingTransition &&
+
+ val canInterceptSwipeTransition =
canChangeScene &&
- offsetAvailable.toAmount() != 0f
+ gestureHandler.isDrivingTransition &&
+ offsetAvailable.toAmount() != 0f
+ if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
+
+ val progress = gestureHandler.swipeTransition.progress
+ val threshold = gestureHandler.layoutImpl.transitionInterceptionThreshold
+ fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
+
+ // The transition is always between 0 and 1. If it is close to either of these
+ // intervals, we want to go directly to the TransitionState.Idle.
+ // The progress value can go beyond this range in the case of overscroll.
+ val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f)
+ if (shouldSnapToIdle) {
+ gestureHandler.swipeTransition.stopOffsetAnimation()
+ gestureHandler.transitionState =
+ TransitionState.Idle(gestureHandler.swipeTransition.currentScene)
+ }
+
+ // Start only if we cannot consume this event
+ !shouldSnapToIdle
},
canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
val amount = offsetAvailable.toAmount()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index efdfe7a..9c31445 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.annotation.FloatRange
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
@@ -41,6 +42,8 @@
* @param transitions the definition of the transitions used to animate a change of scene.
* @param state the observable state of this layout.
* @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
+ * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
+ * intercepted, the progress value must be above the threshold, and below (1 - threshold).
* @param scenes the configuration of the different scenes of this layout.
*/
@Composable
@@ -51,6 +54,7 @@
modifier: Modifier = Modifier,
state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
edgeDetector: EdgeDetector = DefaultEdgeDetector,
+ @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
val density = LocalDensity.current
@@ -63,6 +67,7 @@
state = state,
density = density,
edgeDetector = edgeDetector,
+ transitionInterceptionThreshold = transitionInterceptionThreshold,
coroutineScope = coroutineScope,
)
}
@@ -71,6 +76,7 @@
layoutImpl.transitions = transitions
layoutImpl.density = density
layoutImpl.edgeDetector = edgeDetector
+ layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
layoutImpl.setScenes(scenes)
layoutImpl.setCurrentScene(currentScene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 0b06953..94f2737 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -50,6 +50,7 @@
internal val state: SceneTransitionLayoutState,
density: Density,
edgeDetector: EdgeDetector,
+ transitionInterceptionThreshold: Float,
coroutineScope: CoroutineScope,
) {
internal val scenes = SnapshotStateMap<SceneKey, Scene>()
@@ -62,6 +63,7 @@
internal var transitions by mutableStateOf(transitions)
internal var density: Density by mutableStateOf(density)
internal var edgeDetector by mutableStateOf(edgeDetector)
+ internal var transitionInterceptionThreshold by mutableStateOf(transitionInterceptionThreshold)
private val horizontalGestureHandler: SceneGestureHandler
private val verticalGestureHandler: SceneGestureHandler
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
new file mode 100644
index 0000000..6401bb3
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.compose.animation.scene
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Composable
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun SceneScope.Element(
+ key: ElementKey,
+ size: Dp,
+ offset: Dp,
+ modifier: Modifier = Modifier,
+ onLayout: () -> Unit = {},
+ onPlacement: () -> Unit = {},
+ ) {
+ Box(
+ modifier
+ .offset(offset)
+ .element(key)
+ .intermediateLayout { measurable, constraints ->
+ onLayout()
+ val placement = measurable.measure(constraints)
+ layout(placement.width, placement.height) {
+ onPlacement()
+ placement.place(0, 0)
+ }
+ }
+ .size(size)
+ )
+ }
+
+ @Test
+ fun staticElements_noLayout_noPlacement() {
+ val nFrames = 20
+ val layoutSize = 100.dp
+ val elementSize = 50.dp
+ val elementOffset = 20.dp
+
+ var fooLayouts = 0
+ var fooPlacements = 0
+ var barLayouts = 0
+ var barPlacements = 0
+
+ rule.testTransition(
+ fromSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(
+ TestElements.Foo,
+ elementSize,
+ elementOffset,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
+
+ // Transformed element
+ Element(
+ TestElements.Bar,
+ elementSize,
+ elementOffset,
+ onLayout = { barLayouts++ },
+ onPlacement = { barPlacements++ },
+ )
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(TestElements.Foo, elementSize, elementOffset)
+ }
+ },
+ transition = {
+ spec = tween(nFrames * 16)
+
+ // no-op transformations.
+ translate(TestElements.Bar, x = 0.dp, y = 0.dp)
+ scaleSize(TestElements.Bar, width = 1f, height = 1f)
+ },
+ ) {
+ var numberOfLayoutsAfterOneAnimationFrame = 0
+ var numberOfPlacementsAfterOneAnimationFrame = 0
+
+ fun assertNumberOfLayoutsAndPlacements() {
+ assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(fooPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(barPlacements).isEqualTo(numberOfPlacementsAfterOneAnimationFrame)
+ }
+
+ at(16) {
+ // Capture the number of layouts and placements that happened after 1 animation
+ // frame.
+ numberOfLayoutsAfterOneAnimationFrame = fooLayouts
+ numberOfPlacementsAfterOneAnimationFrame = fooPlacements
+ }
+ repeat(nFrames - 2) { i ->
+ // Ensure that all animation frames (except the final one) don't relayout or replace
+ // static (shared or transformed) elements.
+ at(32L + i * 16) { assertNumberOfLayoutsAndPlacements() }
+ }
+ }
+ }
+
+ @Test
+ fun onlyMovingElements_noLayout_onlyPlacement() {
+ val nFrames = 20
+ val layoutSize = 100.dp
+ val elementSize = 50.dp
+
+ var fooLayouts = 0
+ var fooPlacements = 0
+ var barLayouts = 0
+ var barPlacements = 0
+
+ rule.testTransition(
+ fromSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(
+ TestElements.Foo,
+ elementSize,
+ offset = 0.dp,
+ onLayout = { fooLayouts++ },
+ onPlacement = { fooPlacements++ },
+ )
+
+ // Transformed element
+ Element(
+ TestElements.Bar,
+ elementSize,
+ offset = 0.dp,
+ onLayout = { barLayouts++ },
+ onPlacement = { barPlacements++ },
+ )
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.size(layoutSize)) {
+ // Shared element.
+ Element(TestElements.Foo, elementSize, offset = 20.dp)
+ }
+ },
+ transition = {
+ spec = tween(nFrames * 16)
+
+ // Only translate Bar.
+ translate(TestElements.Bar, x = 20.dp, y = 20.dp)
+ scaleSize(TestElements.Bar, width = 1f, height = 1f)
+ },
+ ) {
+ var numberOfLayoutsAfterOneAnimationFrame = 0
+ var lastNumberOfPlacements = 0
+
+ fun assertNumberOfLayoutsAndPlacements() {
+ // The number of layouts have not changed.
+ assertThat(fooLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+ assertThat(barLayouts).isEqualTo(numberOfLayoutsAfterOneAnimationFrame)
+
+ // The number of placements have increased.
+ assertThat(fooPlacements).isGreaterThan(lastNumberOfPlacements)
+ assertThat(barPlacements).isGreaterThan(lastNumberOfPlacements)
+ lastNumberOfPlacements = fooPlacements
+ }
+
+ at(16) {
+ // Capture the number of layouts and placements that happened after 1 animation
+ // frame.
+ numberOfLayoutsAfterOneAnimationFrame = fooLayouts
+ lastNumberOfPlacements = fooPlacements
+ }
+ repeat(nFrames - 2) { i ->
+ // Ensure that all animation frames (except the final one) only replaced the
+ // elements.
+ at(32L + i * 16) { assertNumberOfLayoutsAndPlacements() }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index 7ab2096..49ef31b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -69,6 +69,8 @@
scene(SceneC) { Text("SceneC") }
}
+ val transitionInterceptionThreshold = 0.05f
+
val sceneGestureHandler =
SceneGestureHandler(
layoutImpl =
@@ -79,6 +81,7 @@
state = layoutState,
density = Density(1f),
edgeDetector = DefaultEdgeDetector,
+ transitionInterceptionThreshold = transitionInterceptionThreshold,
coroutineScope = coroutineScope,
)
.apply { setScenesTargetSizeForTest(LAYOUT_SIZE) },
@@ -107,6 +110,9 @@
val transitionState: TransitionState
get() = layoutState.transitionState
+ val progress: Float
+ get() = (transitionState as Transition).progress
+
fun advanceUntilIdle() {
coroutineScope.testScheduler.advanceUntilIdle()
}
@@ -145,13 +151,12 @@
fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest {
draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
draggable.onDelta(pixels = deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
draggable.onDelta(pixels = deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
}
@Test
@@ -257,8 +262,7 @@
)
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
assertThat(consumed).isEqualTo(offsetY10)
}
@@ -282,13 +286,12 @@
nestedScroll.scroll(available = offsetY10)
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
// start intercept preScroll
val consumed =
nestedScroll.onPreScroll(available = offsetY10, source = NestedScrollSource.Drag)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
// do nothing on postScroll
nestedScroll.onPostScroll(
@@ -296,13 +299,71 @@
available = Offset.Zero,
source = NestedScrollSource.Drag
)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.3f)
assertScene(currentScene = SceneA, isIdle = false)
}
+ private suspend fun TestGestureScope.preScrollAfterSceneTransition(
+ firstScroll: Float,
+ secondScroll: Float
+ ) {
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
+ // start scene transition
+ nestedScroll.scroll(available = Offset(0f, SCREEN_SIZE * firstScroll))
+
+ // stop scene transition (start the "stop animation")
+ nestedScroll.onPreFling(available = Velocity.Zero)
+
+ // a pre scroll event, that could be intercepted by SceneGestureHandler
+ nestedScroll.onPreScroll(Offset(0f, SCREEN_SIZE * secondScroll), NestedScrollSource.Drag)
+ }
+
+ // Float tolerance for comparisons
+ private val tolerance = 0.00001f
+
+ @Test
+ fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest {
+ val first = transitionInterceptionThreshold - tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertScene(SceneA, isIdle = true)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMinInterceptable_interceptPreScrollEvents() = runGestureTest {
+ val first = transitionInterceptionThreshold + tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertThat(progress).isWithin(tolerance).of(first + second)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMaxInterceptable_interceptPreScrollEvents() = runGestureTest {
+ val first = 1f - transitionInterceptionThreshold - tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertThat(progress).isWithin(tolerance).of(first + second)
+ }
+
+ @Test
+ fun scrollAndFling_scrollMoreThanInterceptable_goToIdleOnNextScene() = runGestureTest {
+ val first = 1f - transitionInterceptionThreshold + tolerance
+ val second = 0.01f
+
+ preScrollAfterSceneTransition(firstScroll = first, secondScroll = second)
+
+ assertScene(SceneC, isIdle = true)
+ }
+
@Test
fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll)
@@ -444,24 +505,23 @@
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = Always)
draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
- val transition = transitionState as Transition
draggable.onDelta(deltaInPixels10)
- assertThat(transition.progress).isEqualTo(0.1f)
+ assertThat(progress).isEqualTo(0.1f)
// now we can intercept the scroll events
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.2f)
// this should be ignored, we are scrolling now!
draggable.onDragStopped(velocityThreshold)
assertScene(currentScene = SceneA, isIdle = false)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.3f)
nestedScroll.scroll(available = offsetY10)
- assertThat(transition.progress).isEqualTo(0.4f)
+ assertThat(progress).isEqualTo(0.4f)
nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold))
assertScene(currentScene = SceneC, isIdle = false)
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
index be8fe8c..ff1146e 100644
--- a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
@@ -19,9 +19,14 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
- <path android:fillColor="@color/ksh_key_item_color"
- android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
-3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
-19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
-14z" />
+ <path android:pathData="M5.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M5.5,16.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M16.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M18.5,16.5C18.5,15.4 17.6,14.5 16.5,14.5C15.4,14.5 14.5,15.4 14.5,16.5C14.5,17.6 15.4,18.5 16.5,18.5C17.6,18.5 18.5,17.6 18.5,16.5ZM12.5,16.5C12.5,14.29 14.29,12.5 16.5,12.5C18.71,12.5 20.5,14.29 20.5,16.5C20.5,17.241 20.299,17.934 19.948,18.529L23,21.59L21.59,23L18.529,19.948C17.934,20.299 17.241,20.5 16.5,20.5C14.29,20.5 12.5,18.71 12.5,16.5Z"
+ android:fillColor="@color/ksh_key_item_color"
+ android:fillType="evenOdd" />
</vector>
+
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 08eccbb..4336ccc 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -21,7 +21,7 @@
style="@style/BluetoothTileDialog.Device"
android:layout_width="match_parent"
android:layout_height="@dimen/bluetooth_dialog_device_height"
- android:paddingEnd="24dp"
+ android:paddingEnd="0dp"
android:paddingStart="20dp"
android:layout_marginBottom="4dp">
@@ -86,6 +86,7 @@
android:id="@+id/gear_icon_image"
android:layout_width="0dp"
android:layout_height="24dp"
+ android:paddingEnd="24dp"
android:src="@drawable/ic_settings_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index c11a18b..af29cad 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -120,6 +120,16 @@
android:visibility="gone"
app:constraint_referenced_ids="ic_arrow,see_all_text" />
+ <View
+ android:id="@+id/see_all_clickable_row"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/device_list"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" />
+
<ImageView
android:id="@+id/ic_arrow"
android:layout_marginStart="36dp"
@@ -141,6 +151,8 @@
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:clickable="false"
android:layout_marginStart="0dp"
android:paddingStart="20dp"
android:text="@string/see_all_bluetooth_devices"
@@ -158,6 +170,16 @@
android:visibility="gone"
app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+ <View
+ android:id="@+id/pair_new_device_clickable_row"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_text"
+ app:layout_constraintBottom_toTopOf="@+id/done_button" />
+
<ImageView
android:id="@+id/ic_add"
android:layout_width="24dp"
@@ -180,6 +202,8 @@
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:clickable="false"
android:layout_marginStart="0dp"
android:paddingStart="20dp"
android:text="@string/pair_new_bluetooth_devices"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9a3c6d5..3163533 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -467,6 +467,10 @@
<!-- Content description of the bluetooth device settings gear icon. [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_device_settings_gear">Click to configure device detail</string>
+ <!-- Content description of the bluetooth device settings see all. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_settings_see_all">Click to see all devices</string>
+ <!-- Content description of the bluetooth device settings pair new device. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_bluetooth_device_settings_pair_new_device">Click to pair new device</string>
<!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_unknown">Battery percentage unknown.</string>
@@ -640,6 +644,10 @@
<string name="quick_settings_bluetooth_device_connected">Connected</string>
<!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]-->
<string name="quick_settings_bluetooth_device_saved">Saved</string>
+ <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]-->
+ <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect">disconnect</string>
+ <!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]-->
+ <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string>
<!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 6082fb9..8ad32b4 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -25,7 +25,11 @@
UDFPS_ULTRASONIC,
UDFPS_OPTICAL,
POWER_BUTTON,
- HOME_BUTTON
+ HOME_BUTTON;
+
+ fun isUdfps(): Boolean {
+ return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
+ }
}
/** Convert [this] to corresponding [FingerprintSensorType] */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 8e13b9c..36e18b3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -106,6 +107,7 @@
private final DozeParameters mDozeParameters;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
+ private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -180,6 +182,7 @@
LockscreenSmartspaceController smartspaceController,
ConfigurationController configurationController,
ScreenOffAnimationController screenOffAnimationController,
+ StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main DelayableExecutor uiExecutor,
@@ -203,6 +206,7 @@
mSmartspaceController = smartspaceController;
mConfigurationController = configurationController;
mScreenOffAnimationController = screenOffAnimationController;
+ mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
@@ -606,6 +610,7 @@
mAodIconsViewModel,
mConfigurationState,
mConfigurationController,
+ mIconViewBindingFailureTracker,
mAodIconViewStore);
final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
nic,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index ee3a55f..7769dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -108,6 +108,9 @@
/** The minimal length of a pattern. */
val minPatternLength: Int
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean>
+
/**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge needs to be completed in order to unlock an otherwise locked device.
@@ -212,6 +215,12 @@
override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE
+ override val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ refreshingFlow(
+ initialValue = true,
+ getFreshValue = { userId -> lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) },
+ )
+
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return withContext(backgroundDispatcher) {
blockingAuthenticationMethodInternal(userRepository.selectedUserId)
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1ede530..5eefbf5 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -145,6 +145,9 @@
val authenticationChallengeResult: SharedFlow<Boolean> =
repository.authenticationChallengeResult
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> = repository.isPinEnhancedPrivacyEnabled
+
private var throttlingCountdownJob: Job? = null
init {
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 4e1cddc..ff36839 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
@@ -92,6 +92,10 @@
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
+ /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
+ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ authenticationInteractor.isPinEnhancedPrivacyEnabled
+
/** Whether the user switcher should be displayed within the bouncer UI on large screens. */
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 2ed0d5d..b2b8049 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -84,6 +84,19 @@
override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
+ /**
+ * Whether the digit buttons should be animated when touched. Note that this doesn't affect the
+ * delete or enter buttons; those should always animate.
+ */
+ val isDigitButtonAnimationEnabled: StateFlow<Boolean> =
+ interactor.isPinEnhancedPrivacyEnabled
+ .map { !it }
+ .stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !interactor.isPinEnhancedPrivacyEnabled.value,
+ )
+
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
val pinInput = mutablePinInput.value
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 508f52c..771dfbc 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -141,6 +141,7 @@
private val umoContent: Flow<List<CommunalContentModel.Umo>> =
mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
if (mediaPlaying) {
+ // TODO(b/310254801): support HALF and FULL layouts
flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
} else {
flowOf(emptyList())
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 9fc86ad..1f2621d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.statusbar.ui.binder.StatusBarViewBinderModule;
import com.android.systemui.statusbar.window.StatusBarWindowModule;
import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
@@ -215,6 +216,7 @@
StatusBarModule.class,
StatusBarPipelineModule.class,
StatusBarPolicyModule.class,
+ StatusBarViewBinderModule.class,
StatusBarWindowModule.class,
SystemPropertiesFlagsModule.class,
SysUIConcurrencyModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index c3f3529..298811b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.scene.shared.model.SceneModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
@@ -46,6 +47,7 @@
* Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless
* of the authentication method used.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class DeviceEntryInteractor
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
new file mode 100644
index 0000000..72b9da6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for device entry under-display fingerprint state changes. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryUdfpsInteractor
+@Inject
+constructor(
+ // TODO (b/309655554): create & use interactors for these repositories
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
+ fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ biometricSettingsRepository: BiometricSettingsRepository,
+) {
+ /** Whether the device supports an under display fingerprint sensor. */
+ val isUdfpsSupported: Flow<Boolean> =
+ fingerprintPropertyRepository.sensorType.map { it.isUdfps() }
+
+ /** Whether the under-display fingerprint sensor is enrolled and enabled for device entry. */
+ val isUdfpsEnrolledAndEnabled: Flow<Boolean> =
+ combine(isUdfpsSupported, biometricSettingsRepository.isFingerprintEnrolledAndEnabled) {
+ udfps,
+ fpEnrolledAndEnabled ->
+ udfps && fpEnrolledAndEnabled
+ }
+ /** Whether the under display fingerprint sensor is currently running. */
+ val isListeningForUdfps =
+ isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ fingerprintAuthRepository.isRunning
+ } else {
+ flowOf(false)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f208fcb..ad3d6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -308,11 +308,6 @@
val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
releasedFlag("smartspace_shared_element_transition_enabled")
- // TODO(b/258517050): Clean up after the feature is launched.
- @JvmField
- val SMARTSPACE_DATE_WEATHER_DECOUPLED =
- sysPropBooleanFlag("persist.sysui.ss.dw_decoupled", default = true)
-
// TODO(b/270223352): Tracking Bug
@JvmField
val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = releasedFlag("hide_smartspace_on_dream_overlay")
@@ -433,9 +428,6 @@
// TODO(b/270437894): Tracking Bug
val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
- // TODO(b/304506662): Tracking Bug
- val MEDIA_DEVICE_NAME_FIX = releasedFlag("media_device_name_fix")
-
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 8b93b17..331d892 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -55,6 +55,7 @@
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -94,6 +95,7 @@
KeyguardStatusViewComponent.class,
KeyguardUserSwitcherComponent.class},
includes = {
+ DeviceEntryIconTransitionModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 8bf2bc3..cc1cf91 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -50,14 +50,18 @@
private val configurationRepository: ConfigurationRepository,
private val keyguardInteractor: KeyguardInteractor,
) {
- val udfpsBurnInXOffset: StateFlow<Int> =
+ val deviceEntryIconXOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
- val udfpsBurnInYOffset: StateFlow<Int> =
+ val deviceEntryIconYOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
- val udfpsBurnInProgress: StateFlow<Float> =
+ val udfpsProgress: StateFlow<Float> =
keyguardInteractor.dozeTimeTick
.mapLatest { burnInHelperWrapper.burnInProgressOffset() }
- .stateIn(scope, SharingStarted.Lazily, burnInHelperWrapper.burnInProgressOffset())
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ burnInHelperWrapper.burnInProgressOffset()
+ )
val keyguardBurnIn: Flow<BurnInModel> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index a331a66..8584401 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -113,7 +113,9 @@
}
companion object {
- val TO_LOCKSCREEN_DURATION = 500.milliseconds
private val DEFAULT_DURATION = 500.milliseconds
+ val TO_LOCKSCREEN_DURATION = 500.milliseconds
+ val TO_GONE_DURATION = DEFAULT_DURATION
+ val TO_OCCLUDED_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index e9719e7..eca7088 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -26,11 +26,11 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromDozingTransitionInteractor
@@ -97,5 +97,6 @@
companion object {
private val DEFAULT_DURATION = 500.milliseconds
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index eace0c7..bd73d60 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -138,6 +138,6 @@
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
- val TO_AOD_DURATION = 1100.milliseconds
+ val TO_AOD_DURATION = 1300.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index ea40ba0..152d217 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -391,5 +391,7 @@
val TO_DREAMING_DURATION = 933.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
val TO_AOD_DURATION = 500.milliseconds
+ val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+ val TO_GONE_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index dec38b5..6a8555c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -142,9 +142,7 @@
::toTriple
)
.collect { (isAsleep, lastStartedStep, isAodAvailable) ->
- if (
- lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep
- ) {
+ if (lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep) {
startTransitionTo(
if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
)
@@ -187,5 +185,6 @@
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 933.milliseconds
+ val TO_AOD_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 24b6661..5f246e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -255,5 +255,7 @@
private val DEFAULT_DURATION = 300.milliseconds
val TO_GONE_DURATION = 500.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
+ val TO_AOD_DURATION = DEFAULT_DURATION
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index c0308e6..f5cd767 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -51,14 +51,14 @@
val scaleForResolution = configRepo.scaleForResolution
/** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
- val burnInOffsets: Flow<BurnInOffsets> =
+ val burnInOffsets: Flow<Offsets> =
combine(
keyguardInteractor.dozeAmount,
- burnInInteractor.udfpsBurnInXOffset,
- burnInInteractor.udfpsBurnInYOffset,
- burnInInteractor.udfpsBurnInProgress
+ burnInInteractor.deviceEntryIconXOffset,
+ burnInInteractor.deviceEntryIconYOffset,
+ burnInInteractor.udfpsProgress
) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
- BurnInOffsets(
+ Offsets(
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
@@ -86,8 +86,8 @@
.onStart { emit(0f) }
}
-data class BurnInOffsets(
- val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
- val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
- val burnInProgress: Float, // current progress based on the aodTransitionAmount
+data class Offsets(
+ val x: Int, // current x burn in offset based on the aodTransitionAmount
+ val y: Int, // current y burn in offset based on the aodTransitionAmount
+ val progress: Float, // current progress based on the aodTransitionAmount
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 9d7477c..d5ad7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -105,4 +105,9 @@
}
.filterNotNull()
}
+
+ /** Immediately (after 1ms) emits the given value for every step of the KeyguardTransition. */
+ fun immediatelyTransitionTo(value: Float): Flow<Float> {
+ return createFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e82ea7f..a8b28bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -24,6 +24,8 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
@@ -34,19 +36,24 @@
object DeviceEntryIconViewBinder {
/**
- * Updates UI for the device entry icon view (lock, unlock and fingerprint icons) and its
- * background.
+ * Updates UI for:
+ * - device entry containing view (parent view for the below views)
+ * - long-press handling view (transparent, no UI)
+ * - foreground icon view (lock/unlock/fingerprint)
+ * - background view (optional)
*/
@SuppressLint("ClickableViewAccessibility")
@JvmStatic
fun bind(
view: DeviceEntryIconView,
viewModel: DeviceEntryIconViewModel,
+ fgViewModel: DeviceEntryForegroundViewModel,
+ bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
) {
- val iconView = view.iconView
- val bgView = view.bgView
val longPressHandlingView = view.longPressHandlingView
+ val fgIconView = view.iconView
+ val bgView = view.bgView
longPressHandlingView.listener =
object : LongPressHandlingView.Listener {
override fun onLongPressDetected(view: View, x: Int, y: Int) {
@@ -56,37 +63,12 @@
viewModel.onLongPress()
}
}
+
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.iconViewModel.collect { iconViewModel ->
- iconView.setImageState(
- view.getIconState(iconViewModel.type, iconViewModel.useAodVariant),
- /* merge */ false
- )
- iconView.imageTintList = ColorStateList.valueOf(iconViewModel.tint)
- iconView.alpha = iconViewModel.alpha
- iconView.setPadding(
- iconViewModel.padding,
- iconViewModel.padding,
- iconViewModel.padding,
- iconViewModel.padding,
- )
- }
- }
- launch {
- viewModel.backgroundViewModel.collect { bgViewModel ->
- bgView.alpha = bgViewModel.alpha
- bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
- }
- }
- launch {
- viewModel.burnInViewModel.collect { burnInViewModel ->
- view.translationX = burnInViewModel.x.toFloat()
- view.translationY = burnInViewModel.y.toFloat()
- view.aodFpDrawable.progress = burnInViewModel.progress
- }
- }
+ // Repeat on CREATED so that the view will always observe the entire
+ // GONE => AOD transition (even though the view may not be visible until the middle
+ // of the transition.
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.isLongPressEnabled.collect { isEnabled ->
longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
@@ -97,6 +79,55 @@
view.accessibilityHintType = hint
}
}
+ launch {
+ viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
+ if (useBackgroundProtection) {
+ bgView.visibility = View.VISIBLE
+ } else {
+ bgView.visibility = View.GONE
+ }
+ }
+ }
+ launch {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
+ view.aodFpDrawable.progress = burnInOffsets.progress
+ }
+ }
+
+ launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
+ }
+ }
+
+ fgIconView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ fgViewModel.viewModel.collect { viewModel ->
+ fgIconView.setImageState(
+ view.getIconState(viewModel.type, viewModel.useAodVariant),
+ /* merge */ false
+ )
+ fgIconView.imageTintList = ColorStateList.valueOf(viewModel.tint)
+ fgIconView.setPadding(
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ )
+ }
+ }
+ }
+ }
+
+ bgView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ bgViewModel.viewModel.collect { bgViewModel ->
+ bgView.alpha = bgViewModel.alpha
+ bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
index 9872d97..52d87d3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -42,9 +42,9 @@
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
- view.progress = burnInOffsets.burnInProgress
- view.translationX = burnInOffsets.burnInXOffset.toFloat()
- view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ view.progress = burnInOffsets.progress
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
index bab04f2..d4621e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -59,8 +59,8 @@
launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
- view.translationX = burnInOffsets.burnInXOffset.toFloat()
- view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
new file mode 100644
index 0000000..b58a80f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.keyguard.ui.transitions
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Each DeviceEntryIconTransition is responsible for updating the given parameters for the current
+ * keyguard transition.
+ * *
+ * MUST list implementing classes in dagger module [DeviceEntryIconTransitionModule].
+ */
+interface DeviceEntryIconTransition {
+ val deviceEntryParentViewAlpha: Flow<Float>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
new file mode 100644
index 0000000..9d557bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.keyguard.ui.transitions
+
+import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Module
+abstract class DeviceEntryIconTransitionModule {
+ @Binds
+ @IntoSet
+ abstract fun aodToLockscreen(
+ impl: AodToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun aodToGone(impl: AodToGoneTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dozingToLockscreen(
+ impl: DozingToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingToLockscreen(
+ impl: DreamingToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToAod(
+ impl: LockscreenToAodTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToDreaming(
+ impl: LockscreenToDreamingTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToOccluded(
+ impl: LockscreenToOccludedTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToPrimaryBouncer(
+ impl: LockscreenToPrimaryBouncerTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenToGone(
+ impl: LockscreenToGoneTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun goneToAod(impl: GoneToAodTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun occludedToAod(impl: OccludedToAodTransitionViewModel): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun occludedToLockscreen(
+ impl: OccludedToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToAod(
+ impl: PrimaryBouncerToAodTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToLockscreen(
+ impl: PrimaryBouncerToLockscreenTransitionViewModel
+ ): DeviceEntryIconTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index c9e3954..af1d0df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -40,7 +40,10 @@
attrs: AttributeSet?,
defStyleAttrs: Int = 0,
) : FrameLayout(context, attrs, defStyleAttrs) {
- val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+ val longPressHandlingView: LongPressHandlingView =
+ LongPressHandlingView(context, attrs) {
+ context.resources.getInteger(R.integer.config_lockIconLongPress).toLong()
+ }
val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
val aodFpDrawable: LottieDrawable = LottieDrawable()
@@ -105,7 +108,7 @@
// FINGERPRINT
animatedIconDrawable.addState(
getIconState(IconType.FINGERPRINT, false),
- context.getDrawable(R.drawable.ic_kg_fingerprint)!!,
+ context.getDrawable(R.drawable.ic_fingerprint)!!,
R.id.locked_fp,
)
@@ -220,7 +223,7 @@
val lp = longPressHandlingView.layoutParams as LayoutParams
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
- longPressHandlingView.setLayoutParams(lp)
+ longPressHandlingView.layoutParams = lp
}
private fun addIconImageView() {
@@ -231,7 +234,7 @@
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
lp.gravity = Gravity.CENTER
- iconView.setLayoutParams(lp)
+ iconView.layoutParams = lp
}
private fun addBgImageView() {
@@ -240,7 +243,7 @@
val lp = bgView.layoutParams as LayoutParams
lp.height = ViewGroup.LayoutParams.MATCH_PARENT
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
- bgView.setLayoutParams(lp)
+ bgView.layoutParams = lp
}
fun getIconState(icon: IconType, aod: Boolean): IntArray {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 7ff4df1..c438e49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -35,9 +35,9 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -50,8 +50,8 @@
private val context: Context,
private val configurationState: ConfigurationState,
private val configurationController: ConfigurationController,
- private val dozeParameters: DozeParameters,
private val featureFlags: FeatureFlagsClassic,
+ private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
private val notificationIconAreaController: NotificationIconAreaController,
@@ -95,6 +95,7 @@
nicAodViewModel,
configurationState,
configurationController,
+ iconBindingFailureTracker,
nicAodIconViewStore,
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index ace970a..13ea8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -36,6 +36,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
@@ -56,12 +58,15 @@
private val featureFlags: FeatureFlags,
private val lockIconViewController: Lazy<LockIconViewController>,
private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+ private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
+ private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!keyguardBottomAreaRefactor() &&
+ if (
+ !keyguardBottomAreaRefactor() &&
!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
) {
return
@@ -87,6 +92,8 @@
DeviceEntryIconViewBinder.bind(
it,
deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..4d2af0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AodToGoneTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromAodTransitionInteractor.TO_GONE_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE),
+ )
+
+ override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 024707a..14de01b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -17,22 +17,29 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class AodToLockscreenTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
@@ -47,4 +54,21 @@
onStart = { 1f },
onStep = { 1f },
)
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ // fade in
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ // background view isn't visible, so return an empty flow
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..06661d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+
+/** Breaks down AOD->OCCLUDED transition into discrete steps for corresponding views to consume. */
+@SysUISingleton
+class AodToOccludedTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED),
+ )
+
+ override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
new file mode 100644
index 0000000..3e8bbb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon background view. */
+@ExperimentalCoroutinesApi
+class DeviceEntryBackgroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+ lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+ aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
+ occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+ occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+) {
+ private val color: Flow<Int> =
+ configurationRepository.onAnyConfigurationChange
+ .map {
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
+ }
+ .onStart {
+ emit(
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.colorSurface
+ )
+ )
+ }
+ private val alpha: Flow<Float> =
+ setOf(
+ lockscreenToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ aodToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ goneToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ primaryBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ occludedToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ occludedToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ dreamingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ )
+ .merge()
+
+ val viewModel: Flow<BackgroundViewModel> =
+ combine(color, alpha) { color, alpha ->
+ BackgroundViewModel(
+ alpha = alpha,
+ tint = color,
+ )
+ }
+
+ data class BackgroundViewModel(
+ val alpha: Float,
+ val tint: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
new file mode 100644
index 0000000..99529a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon foreground view (displayed icon). */
+@ExperimentalCoroutinesApi
+class DeviceEntryForegroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ transitionInteractor: KeyguardTransitionInteractor,
+ deviceEntryIconViewModel: DeviceEntryIconViewModel,
+) {
+ private val isShowingAod: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.map { keyguardState ->
+ keyguardState == KeyguardState.AOD
+ }
+ private val color: Flow<Int> =
+ configurationRepository.onAnyConfigurationChange
+ .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) }
+ .onStart {
+ emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
+ }
+ private val useAodIconVariant: Flow<Boolean> =
+ combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) {
+ isTransitionToAod,
+ isUdfps ->
+ isTransitionToAod && isUdfps
+ }
+ .distinctUntilChanged()
+ private val padding: Flow<Int> =
+ configurationRepository.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+
+ val viewModel: Flow<ForegroundIconViewModel> =
+ combine(
+ deviceEntryIconViewModel.iconType,
+ useAodIconVariant,
+ color,
+ padding,
+ ) { iconType, useAodVariant, color, padding ->
+ ForegroundIconViewModel(
+ type = iconType,
+ useAodVariant = useAodVariant,
+ tint = color,
+ padding = padding,
+ )
+ }
+
+ data class ForegroundIconViewModel(
+ val type: DeviceEntryIconView.IconType,
+ val useAodVariant: Boolean,
+ val tint: Int,
+ val padding: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 842dde3..5b5a103 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -12,57 +12,202 @@
* 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.keyguard.ui.viewmodel
-import android.graphics.Color
+import android.animation.FloatEvaluator
+import android.animation.IntEvaluator
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+/** Models the UI state for the containing device entry icon & long-press handling view. */
@ExperimentalCoroutinesApi
-class DeviceEntryIconViewModel @Inject constructor() {
- // TODO: b/305234447 update these states from the data layer
- val iconViewModel: Flow<IconViewModel> =
- flowOf(
- IconViewModel(
- type = DeviceEntryIconView.IconType.LOCK,
- useAodVariant = false,
- tint = Color.WHITE,
- alpha = 1f,
- padding = 48,
+class DeviceEntryIconViewModel
+@Inject
+constructor(
+ transitions: Set<@JvmSuppressWildcards DeviceEntryIconTransition>,
+ burnInInteractor: BurnInInteractor,
+ shadeInteractor: ShadeInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ transitionInteractor: KeyguardTransitionInteractor,
+ val keyguardInteractor: KeyguardInteractor,
+ val viewModel: AodToLockscreenTransitionViewModel,
+ val shadeDependentFlows: ShadeDependentFlows,
+ private val sceneContainerFlags: SceneContainerFlags,
+ private val keyguardViewController: Lazy<KeyguardViewController>,
+ private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
+ udfpsInteractor: DeviceEntryUdfpsInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+) {
+ private val intEvaluator = IntEvaluator()
+ private val floatEvaluator = FloatEvaluator()
+ private val toAodFromState: Flow<KeyguardState> =
+ transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.from }
+ private val showingAlternateBouncer: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.map { keyguardState ->
+ keyguardState == KeyguardState.ALTERNATE_BOUNCER
+ }
+ private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) }
+ private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) }
+ private val transitionAlpha: Flow<Float> =
+ transitions.map { it.deviceEntryParentViewAlpha }.merge()
+ private val alphaMultiplierFromShadeExpansion: Flow<Float> =
+ combine(
+ showingAlternateBouncer,
+ shadeExpansion,
+ qsProgress,
+ ) { showingAltBouncer, shadeExpansion, qsProgress ->
+ val interpolatedQsProgress = (qsProgress * 2).coerceIn(0f, 1f)
+ if (showingAltBouncer) {
+ 1f
+ } else {
+ (1f - shadeExpansion) * (1f - interpolatedQsProgress)
+ }
+ }
+ // Burn-in offsets in AOD
+ private val nonAnimatedBurnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ burnInInteractor.deviceEntryIconXOffset,
+ burnInInteractor.deviceEntryIconYOffset,
+ burnInInteractor.udfpsProgress
+ ) { fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
+ BurnInOffsets(
+ fullyDozingBurnInX,
+ fullyDozingBurnInY,
+ fullyDozingBurnInProgress,
)
- )
- val backgroundViewModel: Flow<BackgroundViewModel> =
- flowOf(BackgroundViewModel(alpha = 1f, tint = Color.GRAY))
- val burnInViewModel: Flow<BurnInViewModel> = flowOf(BurnInViewModel(0, 0, 0f))
- val isLongPressEnabled: Flow<Boolean> = flowOf(true)
+ }
+ // Burn-in offsets that animate based on the transition amount to AOD
+ private val animatedBurnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ nonAnimatedBurnInOffsets,
+ transitionInteractor.transitionStepsToState(KeyguardState.AOD)
+ ) { burnInOffsets, transitionStepsToAod ->
+ val dozeAmount = transitionStepsToAod.value
+ BurnInOffsets(
+ intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x),
+ intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y),
+ floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress)
+ )
+ }
+
+ val deviceEntryViewAlpha: Flow<Float> =
+ combine(
+ transitionAlpha,
+ alphaMultiplierFromShadeExpansion,
+ ) { alpha, alphaMultiplier ->
+ alpha * alphaMultiplier
+ }
+ val useBackgroundProtection: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+ val burnInOffsets: Flow<BurnInOffsets> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+ if (udfpsEnrolled) {
+ toAodFromState.flatMapLatest { fromState ->
+ when (fromState) {
+ KeyguardState.AOD,
+ KeyguardState.GONE,
+ KeyguardState.OCCLUDED,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ KeyguardState.OFF,
+ KeyguardState.DOZING,
+ KeyguardState.DREAMING,
+ KeyguardState.PRIMARY_BOUNCER -> nonAnimatedBurnInOffsets
+ KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
+ KeyguardState.LOCKSCREEN ->
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = nonAnimatedBurnInOffsets,
+ flowWhenShadeIsNotExpanded = animatedBurnInOffsets,
+ )
+ }
+ }
+ } else {
+ // If UDFPS isn't enrolled, we don't show any UI on AOD so there's no need
+ // to use burn in offsets at all
+ flowOf(BurnInOffsets(x = 0, y = 0, progress = 0f))
+ }
+ }
+ val iconType: Flow<DeviceEntryIconView.IconType> =
+ combine(
+ udfpsInteractor.isListeningForUdfps,
+ deviceEntryInteractor.isUnlocked,
+ ) { isListeningForUdfps, isUnlocked ->
+ if (isUnlocked) {
+ DeviceEntryIconView.IconType.UNLOCK
+ } else {
+ if (isListeningForUdfps) {
+ DeviceEntryIconView.IconType.FINGERPRINT
+ } else {
+ DeviceEntryIconView.IconType.LOCK
+ }
+ }
+ }
+ val isLongPressEnabled: Flow<Boolean> =
+ combine(
+ iconType,
+ deviceEntryUdfpsInteractor.isUdfpsSupported,
+ ) { deviceEntryStatus, isUdfps ->
+ when (deviceEntryStatus) {
+ DeviceEntryIconView.IconType.LOCK -> isUdfps
+ DeviceEntryIconView.IconType.UNLOCK -> true
+ DeviceEntryIconView.IconType.FINGERPRINT -> false
+ }
+ }
val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
- flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+ combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
+ if (longPressEnabled) {
+ deviceEntryStatus.toAccessibilityHintType()
+ } else {
+ DeviceEntryIconView.AccessibilityHintType.NONE
+ }
+ }
fun onLongPress() {
- // TODO() vibrate & perform action based on current lock/unlock state
+ deviceEntryHapticsInteractor.vibrateSuccess()
+
+ // TODO (b/309804148): play auth ripple via an interactor
+
+ if (sceneContainerFlags.isEnabled()) {
+ deviceEntryInteractor.attemptDeviceEntry()
+ } else {
+ keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+ }
}
- data class BurnInViewModel(
- val x: Int, // current x burn in offset based on the aodTransitionAmount
- val y: Int, // current y burn in offset based on the aodTransitionAmount
- val progress: Float, // current progress based on the aodTransitionAmount
- )
- class IconViewModel(
- val type: DeviceEntryIconView.IconType,
- val useAodVariant: Boolean,
- val tint: Int,
- val alpha: Float,
- val padding: Int,
- )
-
- class BackgroundViewModel(
- val alpha: Float,
- val tint: Int,
- )
+ private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
+ DeviceEntryIconView.AccessibilityHintType {
+ return when (this) {
+ DeviceEntryIconView.IconType.LOCK ->
+ DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE
+ DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
+ DeviceEntryIconView.IconType.FINGERPRINT ->
+ DeviceEntryIconView.AccessibilityHintType.NONE
+ }
+ }
}
+
+data class BurnInOffsets(
+ val x: Int, // current x burn in offset based on the aodTransitionAmount
+ val y: Int, // current y burn in offset based on the aodTransitionAmount
+ val progress: Float, // current progress based on the aodTransitionAmount
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..27fb8a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down DOZING->LOCKSCREEN transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DozingToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation: KeyguardTransitionAnimationFlow =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ transitionFlow = interactor.dozingToLockscreenTransition,
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index e24d326..a3b8b85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -18,27 +18,34 @@
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
* consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class DreamingToLockscreenTransitionViewModel
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
-) {
+ private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
+ private val deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
private val transitionAnimation =
@@ -88,4 +95,15 @@
duration = 250.milliseconds,
onStep = { it },
)
+
+ val deviceEntryBackgroundViewAlpha =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ // immediately show; will fade in with deviceEntryParentViewAlpha
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+ override val deviceEntryParentViewAlpha = lockscreenAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 601dbcc..62b2281 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -18,20 +18,27 @@
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
@SysUISingleton
class GoneToAodTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
@@ -60,4 +67,21 @@
onStart = { 0f },
onStep = { it },
)
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+ if (udfpsEnrolled) {
+ // fade in at the end of the transition to give time for FP to start running
+ // and avoid a flicker of the unlocked icon
+ transitionAnimation.createFlow(
+ startTime = 1100.milliseconds,
+ duration = 200.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ emptyFlow()
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
new file mode 100644
index 0000000..2bf12e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow = interactor.lockscreenToAodTransition,
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ ),
+ )
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = // fade in
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ ),
+ flowWhenShadeIsNotExpanded = transitionAnimation.immediatelyTransitionTo(1f),
+ )
+ } else {
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ flowWhenShadeIsNotExpanded = // fade out
+ transitionAnimation.createFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ onFinish = { 0f },
+ ),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index a3ae67d..5229613 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -34,7 +35,8 @@
@Inject
constructor(
interactor: KeyguardTransitionInteractor,
-) {
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_DREAMING_DURATION,
@@ -60,6 +62,12 @@
onStep = { 1f - it },
)
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded = lockscreenAlpha,
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ )
+
companion object {
@JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..59e5aa8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->GONE transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToGoneTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index d3ea89c..d49bc49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -33,8 +34,9 @@
class LockscreenToOccludedTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_OCCLUDED_DURATION,
@@ -59,4 +61,10 @@
interpolator = EMPHASIZED_ACCELERATE,
)
}
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded = lockscreenAlpha,
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
new file mode 100644
index 0000000..f04b67a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToPrimaryBouncerTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
+ )
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ shadeDependentFlows.transitionFlow(
+ flowWhenShadeIsNotExpanded =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1f - it },
+ onFinish = { 0f }
+ ),
+ flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f)
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
new file mode 100644
index 0000000..f7cff9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/** Breaks down OCCLUDED->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludedToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+ ->
+ if (udfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 6845c55..0bdc85d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -18,23 +18,30 @@
import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
* consume.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class OccludedToLockscreenTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
-) {
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor
+) : DeviceEntryIconTransition {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_LOCKSCREEN_DURATION,
@@ -58,4 +65,16 @@
duration = 250.milliseconds,
onStep = { it },
)
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
new file mode 100644
index 0000000..05a6d58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down PRIMARY BOUNCER->AOD transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToAodTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(0f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+ isUdfpsEnrolledAndEnabled ->
+ if (isUdfpsEnrolledAndEnabled) {
+ transitionAnimation.createFlow(
+ duration = 300.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ )
+ } else {
+ emptyFlow()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..3cf793a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ transitionFlow =
+ interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN),
+ )
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+ if (isUdfps) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
new file mode 100644
index 0000000..e45d537
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** Helper for flows that depend on the shade expansion */
+class ShadeDependentFlows
+@Inject
+constructor(
+ transitionInteractor: KeyguardTransitionInteractor,
+ shadeInteractor: ShadeInteractor,
+) {
+ /** When the last keyguard state transition started, was the shade fully expanded? */
+ private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> =
+ transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded)
+
+ /**
+ * Decide which flow to use depending on the shade expansion state at the start of the last
+ * keyguard state transition.
+ */
+ fun <T> transitionFlow(
+ flowWhenShadeIsExpanded: Flow<T>,
+ flowWhenShadeIsNotExpanded: Flow<T>,
+ ): Flow<T> {
+ val filteredFlowWhenShadeIsExpanded =
+ flowWhenShadeIsExpanded
+ .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+ .filter { (_, shadeFullyExpanded) -> shadeFullyExpanded }
+ .map { (valueWhenShadeIsExpanded, _) -> valueWhenShadeIsExpanded }
+ val filteredFlowWhenShadeIsNotExpanded =
+ flowWhenShadeIsNotExpanded
+ .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+ .filter { (_, shadeFullyExpanded) -> !shadeFullyExpanded }
+ .map { (valueWhenShadeIsNotExpanded, _) -> valueWhenShadeIsNotExpanded }
+ return merge(filteredFlowWhenShadeIsExpanded, filteredFlowWhenShadeIsNotExpanded)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
index c10a463..6e77e13e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Context
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.Offsets
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,7 +35,7 @@
val context: Context,
) {
val alpha: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
val isVisible: Flow<Boolean> = alpha.map { it != 0f }
// Padding between the fingerprint icon and its bounding box in pixels.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index 0b1079f..642904d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,9 +19,9 @@
import android.content.Context
import androidx.annotation.ColorInt
import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.Offsets
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -185,7 +185,7 @@
keyguardInteractor,
) {
val dozeAmount: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
// Padding between the fingerprint icon and its bounding box in pixels.
val padding: Flow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 2034d97..dcbf670 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -37,8 +37,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
@@ -70,7 +68,6 @@
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
dumpManager: DumpManager,
- private val featureFlags: FeatureFlagsClassic,
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -392,13 +389,6 @@
)
}
- if (!featureFlags.isEnabled(Flags.MEDIA_DEVICE_NAME_FIX)) {
- if (controller == null || routingSession != null) {
- return routingSession?.name?.toString() ?: device?.name
- }
- return null
- }
-
if (controller == null) {
// In resume state, we don't have a controller - just use the device name
return device?.name
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 79aedff..9f5e1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -35,6 +35,7 @@
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
@@ -94,6 +95,9 @@
@VisibleForTesting
SparseArray<NavigationBar> mNavigationBars = new SparseArray<>();
+ /** Local cache for {@link IWindowManager#hasNavigationBar(int)}. */
+ private SparseBooleanArray mHasNavBar = new SparseBooleanArray();
+
// Tracks config changes that will actually recreate the nav bar
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE
@@ -221,10 +225,16 @@
}
private boolean shouldCreateNavBarAndTaskBar(int displayId) {
+ if (mHasNavBar.indexOfKey(displayId) > -1) {
+ return mHasNavBar.get(displayId);
+ }
+
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
try {
- return wms.hasNavigationBar(displayId);
+ boolean hasNavigationBar = wms.hasNavigationBar(displayId);
+ mHasNavBar.put(displayId, hasNavigationBar);
+ return hasNavigationBar;
} catch (RemoteException e) {
// Cannot get wms, just return false with warning message.
Log.w(TAG, "Cannot get WindowManager.");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
index 736f7cf..ae554d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -26,6 +26,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.user.data.repository.UserRepository
@@ -60,12 +61,17 @@
) : QSTileViewModelFactory<T> {
/**
- * Creates [QSTileViewModelImpl] based on the interactors obtained from [component].
- * Reference of that [component] is then stored along the view model.
+ * Creates [QSTileViewModelImpl] based on the interactors obtained from [QSTileComponent].
+ * Reference of that [QSTileComponent] is then stored along the view model.
*/
- fun create(tileSpec: TileSpec, component: QSTileComponent<T>): QSTileViewModelImpl<T> =
- QSTileViewModelImpl(
- qsTileConfigProvider.getConfig(tileSpec.spec),
+ fun create(
+ tileSpec: TileSpec,
+ componentFactory: (config: QSTileConfig) -> QSTileComponent<T>
+ ): QSTileViewModelImpl<T> {
+ val config = qsTileConfigProvider.getConfig(tileSpec.spec)
+ val component = componentFactory(config)
+ return QSTileViewModelImpl(
+ config,
component::userActionInteractor,
component::dataInteractor,
component::dataToStateMapper,
@@ -77,6 +83,7 @@
systemClock,
backgroundDispatcher,
)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 12a083e..5e19439 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -116,7 +116,7 @@
)
override fun forceUpdate() {
- forceUpdates.tryEmit(Unit)
+ tileScope.launch { forceUpdates.emit(Unit) }
}
override fun onUserChanged(user: UserHandle) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 7d7af64..27007bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -19,6 +19,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
+import com.android.systemui.qs.tiles.impl.custom.di.QSTileConfigModule
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
@@ -34,18 +39,31 @@
private val adapterFactory: QSTileViewModelAdapter.Factory,
private val tileMap:
Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+ private val customTileComponentBuilder: CustomTileComponent.Builder,
+ private val customTileViewModelFactory: QSTileViewModelFactory.Component<CustomTileDataModel>,
) : QSFactory {
init {
for (viewModelTileSpec in tileMap.keys) {
- // throws an exception when there is no config for a tileSpec of an injected viewModel
- qsTileConfigProvider.getConfig(viewModelTileSpec)
+ require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) {
+ "No config for $viewModelTileSpec"
+ }
}
}
- override fun createTile(tileSpec: String): QSTile? =
- tileMap[tileSpec]?.let {
- val tile = it.get()
- adapterFactory.create(tile)
+ override fun createTile(tileSpec: String): QSTile? {
+ val viewModel: QSTileViewModel =
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
+ is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
+ is TileSpec.Invalid -> null
+ }
+ ?: return null
+ return adapterFactory.create(viewModel)
+ }
+
+ private fun createCustomTileViewModel(spec: TileSpec.CustomTileSpec): QSTileViewModel =
+ customTileViewModelFactory.create(spec) { config ->
+ customTileComponentBuilder.qsTileConfigModule(QSTileConfigModule(config)).build()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 7b4b557..5bdb592 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -20,9 +20,12 @@
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
+import android.view.View.AccessibilityDelegate
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
@@ -32,13 +35,18 @@
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.time.SystemClock
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.withContext
/** Dialog for showing active, connected and saved bluetooth devices. */
@SysUISingleton
@@ -47,6 +55,7 @@
private val bluetoothToggleInitialValue: Boolean,
private val subtitleResIdInitialValue: Int,
private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
@@ -65,13 +74,17 @@
private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback)
+ private var lastUiUpdateMs: Long = -1
+
+ private var lastItemRow: Int = -1
+
private lateinit var toggleView: Switch
private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
private lateinit var seeAllViewGroup: View
private lateinit var pairNewDeviceViewGroup: View
- private lateinit var seeAllText: View
- private lateinit var pairNewDeviceText: View
+ private lateinit var seeAllRow: View
+ private lateinit var pairNewDeviceRow: View
private lateinit var deviceListView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
@@ -88,8 +101,8 @@
doneButton = requireViewById(R.id.done_button)
seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
- seeAllText = requireViewById(R.id.see_all_text)
- pairNewDeviceText = requireViewById(R.id.pair_new_device_text)
+ seeAllRow = requireViewById(R.id.see_all_clickable_row)
+ pairNewDeviceRow = requireViewById(R.id.pair_new_device_clickable_row)
deviceListView = requireViewById<RecyclerView>(R.id.device_list)
setupToggle()
@@ -97,22 +110,37 @@
subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
- seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceText.setOnClickListener {
+ seeAllRow.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceRow.setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
}
- internal fun onDeviceItemUpdated(
+ override fun start() {
+ lastUiUpdateMs = systemClock.elapsedRealtime()
+ }
+
+ internal suspend fun onDeviceItemUpdated(
deviceItem: List<DeviceItem>,
showSeeAll: Boolean,
showPairNewDevice: Boolean
) {
- val start = systemClock.elapsedRealtime()
- deviceItemAdapter.refreshDeviceItemList(deviceItem) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
- logger.logDeviceUiUpdate(systemClock.elapsedRealtime() - start)
+ withContext(mainDispatcher) {
+ val start = systemClock.elapsedRealtime()
+ val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt()
+ // Add a slight delay for smoother dialog height change
+ if (itemRow != lastItemRow) {
+ delay(MIN_HEIGHT_CHANGE_INTERVAL_MS - (start - lastUiUpdateMs))
+ }
+ if (isActive) {
+ deviceItemAdapter.refreshDeviceItemList(deviceItem) {
+ seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ lastUiUpdateMs = systemClock.elapsedRealtime()
+ lastItemRow = itemRow
+ logger.logDeviceUiUpdate(lastUiUpdateMs - start)
+ }
+ }
}
}
@@ -169,7 +197,8 @@
deviceItem1.iconWithDescription?.second ==
deviceItem2.iconWithDescription?.second &&
deviceItem1.background == deviceItem2.background &&
- deviceItem1.isEnabled == deviceItem2.isEnabled
+ deviceItem1.isEnabled == deviceItem2.isEnabled &&
+ deviceItem1.actionAccessibilityLabel == deviceItem2.actionAccessibilityLabel
}
}
@@ -213,6 +242,21 @@
mutableDeviceItemClick.tryEmit(item)
uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
}
+ accessibilityDelegate =
+ object : AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.addAction(
+ AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ item.actionAccessibilityLabel
+ )
+ )
+ }
+ }
}
nameView.text = item.deviceName
summaryView.text = item.connectionSummary
@@ -230,6 +274,7 @@
}
internal companion object {
+ const val MIN_HEIGHT_CHANGE_INTERVAL_MS = 800L
const val MAX_DEVICE_ITEM_ENTRY = 3
const val ACTION_BLUETOOTH_DEVICE_DETAILS =
"com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
@@ -238,5 +283,9 @@
const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
const val DISABLED_ALPHA = 0.3f
const val ENABLED_ALPHA = 1f
+
+ private fun Boolean.toInt(): Int {
+ return if (this) 1 else 0
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index f7e0de3..34c2aba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -40,7 +40,6 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -76,6 +75,7 @@
dismissDialog()
var updateDeviceItemJob: Job? = null
+ var updateDialogUiJob: Job? = null
job =
coroutineScope.launch(mainDispatcher) {
@@ -93,10 +93,9 @@
)
}
?: dialog!!.show()
+
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
- // Add a slight delay for smoother dialog bounds change
- delay(FIRST_LOAD_DELAY_MS)
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
@@ -128,11 +127,14 @@
deviceItemInteractor.deviceItemUpdate
.onEach {
- dialog!!.onDeviceItemUpdated(
- it.take(MAX_DEVICE_ITEM_ENTRY),
- showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
- showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
- )
+ updateDialogUiJob?.cancel()
+ updateDialogUiJob = launch {
+ dialog?.onDeviceItemUpdated(
+ it.take(MAX_DEVICE_ITEM_ENTRY),
+ showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
+ showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
+ )
+ }
}
.launchIn(this)
@@ -153,6 +155,7 @@
bluetoothStateInteractor.isBluetoothEnabled,
getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled),
this@BluetoothTileDialogViewModel,
+ mainDispatcher,
systemClock,
uiEventLogger,
logger,
@@ -205,7 +208,6 @@
companion object {
private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
- private const val FIRST_LOAD_DELAY_MS = 500L
private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
else R.string.bt_is_off
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
index 2c8d2a0..1c621b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
@@ -49,5 +49,6 @@
val connectionSummary: String = "",
val iconWithDescription: Pair<Drawable, String>? = null,
val background: Int? = null,
- var isEnabled: Boolean = true
+ var isEnabled: Boolean = true,
+ var actionAccessibilityLabel: String = "",
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
index 7bb1619..1c9be0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -28,6 +28,10 @@
private val backgroundOffBusy = R.drawable.bluetooth_tile_dialog_bg_off_busy
private val connected = R.string.quick_settings_bluetooth_device_connected
private val saved = R.string.quick_settings_bluetooth_device_saved
+private val actionAccessibilityLabelActivate =
+ R.string.accessibility_quick_settings_bluetooth_device_tap_to_activate
+private val actionAccessibilityLabelDisconnect =
+ R.string.accessibility_quick_settings_bluetooth_device_tap_to_disconnect
/** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */
internal abstract class DeviceItemFactory {
@@ -60,6 +64,7 @@
},
background = backgroundOn,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
)
}
}
@@ -87,6 +92,7 @@
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
)
}
}
@@ -112,6 +118,7 @@
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelDisconnect),
)
}
}
@@ -137,6 +144,7 @@
},
background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
+ actionAccessibilityLabel = context.getString(actionAccessibilityLabelActivate),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
index 761274e..14bf25d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
@@ -19,17 +19,18 @@
import android.os.UserHandle
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@QSTileScope
-class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileData> {
+class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileDataModel> {
override fun tileData(
user: UserHandle,
triggers: Flow<DataUpdateTrigger>
- ): Flow<CustomTileData> {
+ ): Flow<CustomTileDataModel> {
TODO("Not yet implemented")
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
index f7bec02..e23a5c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
@@ -17,15 +17,16 @@
package com.android.systemui.qs.tiles.impl.custom
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import javax.inject.Inject
@QSTileScope
-class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileData> {
+class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileDataModel> {
- override fun map(config: QSTileConfig, data: CustomTileData): QSTileState {
+ override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
index 6c1c1a3..f34704b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
@@ -18,14 +18,15 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
@QSTileScope
class CustomTileUserActionInteractor @Inject constructor() :
- QSTileUserActionInteractor<CustomTileData> {
+ QSTileUserActionInteractor<CustomTileDataModel> {
- override suspend fun handleInput(input: QSTileInput<CustomTileData>) {
+ override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
index 01df906..88bc8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -16,13 +16,14 @@
package com.android.systemui.qs.tiles.impl.custom.di
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import dagger.Subcomponent
@QSTileScope
@Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
-interface CustomTileComponent : QSTileComponent<Any> {
+interface CustomTileComponent : QSTileComponent<CustomTileDataModel> {
@Subcomponent.Builder
interface Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 482bf9b..83767aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -19,13 +19,13 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.custom.CustomTileData
import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
import dagger.Module
@@ -36,15 +36,15 @@
@Binds
fun bindDataInteractor(
dataInteractor: CustomTileInteractor
- ): QSTileDataInteractor<CustomTileData>
+ ): QSTileDataInteractor<CustomTileDataModel>
@Binds
fun bindUserActionInteractor(
userActionInteractor: CustomTileUserActionInteractor
- ): QSTileUserActionInteractor<CustomTileData>
+ ): QSTileUserActionInteractor<CustomTileDataModel>
@Binds
- fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData>
+ fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileDataModel>
@Binds
fun bindCustomTileDefaultsRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
index bb5a229..f095c01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.domain.entity
import android.content.ComponentName
import android.graphics.drawable.Icon
@@ -22,12 +22,11 @@
import android.service.quicksettings.Tile
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
-data class CustomTileData(
+data class CustomTileDataModel(
val user: UserHandle,
val componentName: ComponentName,
val tile: Tile,
val callingAppUid: Int,
- val isActive: Boolean,
val hasPendingBind: Boolean,
val shouldShowChevron: Boolean,
val defaultTileLabel: CharSequence?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
index 3f3b94e..0609e79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -18,20 +18,31 @@
import com.android.internal.util.Preconditions
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
interface QSTileConfigProvider {
/**
- * Returns a [QSTileConfig] for a [tileSpec] or throws [IllegalArgumentException] if there is no
- * config for such [tileSpec].
+ * Returns a [QSTileConfig] for a [tileSpec]:
+ * - injected config for [TileSpec.PlatformTileSpec] or throws [IllegalArgumentException] if
+ * there is none
+ * - new config for [TileSpec.CustomTileSpec].
+ * - throws [IllegalArgumentException] for [TileSpec.Invalid]
*/
fun getConfig(tileSpec: String): QSTileConfig
+
+ fun hasConfig(tileSpec: String): Boolean
}
@SysUISingleton
-class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<String, QSTileConfig>) :
- QSTileConfigProvider {
+class QSTileConfigProviderImpl
+@Inject
+constructor(
+ private val configs: Map<String, QSTileConfig>,
+ private val qsEventLogger: QsEventLogger,
+) : QSTileConfigProvider {
init {
for (entry in configs.entries) {
@@ -44,6 +55,26 @@
}
}
+ override fun hasConfig(tileSpec: String): Boolean =
+ when (TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> configs.containsKey(tileSpec)
+ is TileSpec.CustomTileSpec -> true
+ is TileSpec.Invalid -> false
+ }
+
override fun getConfig(tileSpec: String): QSTileConfig =
- configs[tileSpec] ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> {
+ configs[tileSpec]
+ ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ }
+ is TileSpec.CustomTileSpec ->
+ QSTileConfig(
+ spec,
+ QSTileUIConfig.Empty,
+ qsEventLogger.getNewInstanceId(),
+ )
+ is TileSpec.Invalid ->
+ throw IllegalArgumentException("TileSpec.Invalid doesn't support configs")
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index ab0d6e3..922560f 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -17,11 +17,10 @@
package com.android.systemui.smartspace.config
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
class BcSmartspaceConfigProvider(private val featureFlags: FeatureFlags) :
BcSmartspaceConfigPlugin {
override val isDefaultDateWeatherDisabled: Boolean
- get() = featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)
+ get() = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 2cd5560..ef87406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -269,8 +269,7 @@
fun isDateWeatherDecoupled(): Boolean {
execution.assertIsMainThread()
- return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
- datePlugin != null && weatherPlugin != null
+ return datePlugin != null && weatherPlugin != null
}
fun isWeatherEnabled(): Boolean {
@@ -501,8 +500,8 @@
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
- if (isDateWeatherDecoupled()) {
- return t.featureType != SmartspaceTarget.FEATURE_WEATHER
+ if (isDateWeatherDecoupled() && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
+ return false
}
if (!showNotifications) {
return t.featureType == SmartspaceTarget.FEATURE_WEATHER
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 2ea7f61..f8bc0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -23,27 +23,37 @@
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
import com.android.internal.policy.SystemBarUtils
+import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.icon.IconPack
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
-import com.android.systemui.util.children
+import com.android.systemui.util.asIndenting
import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.stateFlow
+import com.android.systemui.util.printCollection
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
+import dagger.Binds
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
@@ -62,11 +72,18 @@
viewModel: NotificationIconContainerShelfViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: ShelfNotificationIconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
lifecycleScope.launch {
- viewModel.icons.bindIcons(view, configuration, configurationController, viewStore)
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ configurationController,
+ notifyBindingFailures = { failureTracker.shelfFailures = it },
+ viewStore,
+ )
}
}
}
@@ -77,18 +94,20 @@
viewModel: NotificationIconContainerStatusBarViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: StatusBarNotificationIconViewStore,
): DisposableHandle {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
return view.repeatWhenAttached {
lifecycleScope.run {
launch {
- val iconColors =
+ val iconColors: Flow<NotificationIconColors> =
viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
viewModel.icons.bindIcons(
view,
configuration,
configurationController,
+ notifyBindingFailures = { failureTracker.statusBarFailures = it },
viewStore,
) { _, sbiv ->
StatusBarIconViewBinder.bindIconColors(
@@ -110,6 +129,7 @@
viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ failureTracker: StatusBarIconViewBindingFailureTracker,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
@@ -119,6 +139,7 @@
view,
configuration,
configurationController,
+ notifyBindingFailures = { failureTracker.aodFailures = it },
viewStore,
) { _, sbiv ->
viewModel.bindAodStatusBarIconView(sbiv, configuration)
@@ -176,7 +197,7 @@
}
/**
- * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children].
+ * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s children.
*
* [bindIcon] will be invoked to bind a child [StatusBarIconView] to an icon associated with the
* given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
@@ -186,6 +207,7 @@
view: NotificationIconContainer,
configuration: ConfigurationState,
configurationController: ConfigurationController,
+ notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
): Unit = coroutineScope {
@@ -208,57 +230,59 @@
FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
}
- launch {
- layoutParams.collect { params: FrameLayout.LayoutParams ->
- for (child in view.children) {
- child.layoutParams = params
- }
- }
- }
-
- val iconBindings = mutableMapOf<String, Job>()
+ val failedBindings = mutableSetOf<String>()
+ val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
var prevIcons = NotificationIconsViewData()
- sample(layoutParams, ::Pair).collect {
- (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
- ->
+ collect { iconsData: NotificationIconsViewData ->
val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
prevIcons = iconsData
- val replacingIcons =
- iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, v) ->
- viewStore.iconView(v.notifKey).statusBarIcon
+ val replacingIcons: ArrayMap<String, StatusBarIcon> =
+ iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, info) ->
+ boundViewsByNotifKey[info.notifKey]?.first?.statusBarIcon
}
view.setReplacingIcons(replacingIcons)
- val childrenByNotifKey: Map<String, StatusBarIconView> =
- view.children.filterIsInstance<StatusBarIconView>().associateByTo(ArrayMap()) {
- it.notification.key
- }
-
- iconsDiff.removed
- .mapNotNull { key -> childrenByNotifKey[key]?.let { key to it } }
- .forEach { (key, child) ->
- view.removeView(child)
- iconBindings.remove(key)?.cancel()
- }
-
- val toAdd = iconsDiff.added.map { it.notifKey to viewStore.iconView(it.notifKey) }
- for ((i, keyAndView) in toAdd.withIndex()) {
- val (key, sbiv) = keyAndView
- // The view might still be transiently added if it was just removed
- // and added again
- view.removeTransientView(sbiv)
- view.addView(sbiv, i, layoutParams)
- iconBindings.remove(key)?.cancel()
- iconBindings[key] = launch { bindIcon(key, sbiv) }
+ for (notifKey in iconsDiff.removed) {
+ failedBindings.remove(notifKey)
+ val (child, job) = boundViewsByNotifKey.remove(notifKey) ?: continue
+ view.removeView(child)
+ job.cancel()
}
+ val toAdd: Sequence<String> =
+ iconsDiff.added.asSequence().map { it.notifKey } + failedBindings
+ for ((idx, notifKey) in toAdd.withIndex()) {
+ val sbiv = viewStore.iconView(notifKey)
+ if (sbiv == null) {
+ failedBindings.add(notifKey)
+ continue
+ }
+ // The view might still be transiently added if it was just removed and added again
+ view.removeTransientView(sbiv)
+ view.addView(sbiv, idx)
+ boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
+ boundViewsByNotifKey[notifKey] =
+ Pair(
+ sbiv,
+ launch {
+ launch { layoutParams.collect { sbiv.layoutParams = it } }
+ bindIcon(notifKey, sbiv)
+ },
+ )
+ }
+
+ notifyBindingFailures(failedBindings)
+
view.setChangingViewPositions(true)
+
// Re-sort notification icons
+ val expectedChildren =
+ iconsData.visibleKeys.mapNotNull { boundViewsByNotifKey[it.notifKey]?.first }
val childCount = view.childCount
for (i in 0 until childCount) {
val actual = view.getChildAt(i)
- val expected = viewStore.iconView(iconsData.visibleKeys[i].notifKey)
+ val expected = expectedChildren[i]
if (actual === expected) {
continue
}
@@ -273,47 +297,30 @@
/** External storage for [StatusBarIconView] instances. */
fun interface IconViewStore {
- fun iconView(key: String): StatusBarIconView
+ fun iconView(key: String): StatusBarIconView?
}
@ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
}
/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
-class ShelfNotificationIconViewStore
-@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.shelfIcon ?: error("No shelf IconView found for key: $key")
- }
-}
+class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
/** [IconViewStore] for the always-on display. */
class AlwaysOnDisplayNotificationIconViewStore
@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.aodIcon ?: error("No AOD IconView found for key: $key")
- }
-}
+constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
/** [IconViewStore] for the status bar. */
-class StatusBarNotificationIconViewStore
-@Inject
-constructor(
- private val notifCollection: NotifCollection,
-) : IconViewStore {
- override fun iconView(key: String): StatusBarIconView {
- val entry = notifCollection.getEntry(key) ?: error("No entry found for key: $key")
- return entry.icons.statusBarIcon ?: error("No status bar IconView found for key: $key")
+class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
+
+private fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
+ IconViewStore { key ->
+ getEntry(key)?.icons?.let(block)
}
-}
private val View.viewBounds: Rect
get() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt
new file mode 100644
index 0000000..0c114a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBindingFailureTracker.kt
@@ -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 com.android.systemui.statusbar.notification.icon.ui.viewbinder
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import dagger.Binds
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import java.io.PrintWriter
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarIconViewBindingFailureTracker @Inject constructor() : CoreStartable {
+
+ var aodFailures: Collection<String> = emptyList()
+ var statusBarFailures: Collection<String> = emptyList()
+ var shelfFailures: Collection<String> = emptyList()
+
+ // TODO(b/310681665): Ideally we wouldn't need to implement CoreStartable at all, and could just
+ // @Binds @IntoSet the Dumpable.
+ override fun start() {
+ // no-op, we're just using this as a dumpable
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ if (!NotificationIconContainerRefactor.isEnabled) return
+ pw.asIndenting().run {
+ printCollection("AOD Icon binding failures:", aodFailures)
+ printCollection("Status Bar Icon binding failures:", statusBarFailures)
+ printCollection("Shelf Icon binding failures:", shelfFailures)
+ }
+ }
+
+ @dagger.Module
+ interface Module {
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBarIconViewBindingFailureTracker::class)
+ fun bindStartable(impl: StatusBarIconViewBindingFailureTracker): CoreStartable
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 82626acc..c03a4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -31,6 +31,7 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -83,19 +84,18 @@
/** An Icon to show "isolated" in the IconContainer. */
val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
headsUpIconInteractor.isolatedNotification
+ .combine(icons) { isolatedNotif, iconsViewData ->
+ isolatedNotif?.let {
+ iconsViewData.visibleKeys.firstOrNull { it.notifKey == isolatedNotif }
+ }
+ }
.pairwise(initialValue = null)
- .sample(combine(icons, shadeInteractor.shadeExpansion, ::Pair)) {
- (prev, isolatedNotif),
- (iconsViewData, shadeExpansion),
- ->
- val iconInfo =
- isolatedNotif?.let {
- iconsViewData.visibleKeys.firstOrNull { it.notifKey == isolatedNotif }
- }
+ .distinctUntilChanged()
+ .sample(shadeInteractor.shadeExpansion) { (prev, iconInfo), shadeExpansion ->
val animate =
when {
- isolatedNotif == prev -> false
- isolatedNotif == null || prev == null -> shadeExpansion == 0f
+ iconInfo?.notifKey == prev?.notifKey -> false
+ iconInfo == null || prev == null -> shadeExpansion == 0f
else -> false
}
AnimatableEvent(iconInfo, animate)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 538be14..9ff416a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -43,9 +43,9 @@
class PeekDisabledSuppressor(
private val globalSettings: GlobalSettings,
private val headsUpManager: HeadsUpManager,
- private val logger: NotificationInterruptLogger,
+ private val logger: VisualInterruptionDecisionLogger,
@Main private val mainHandler: Handler,
-) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") {
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek disabled by global setting") {
private var isEnabled = false
override fun shouldSuppress(): Boolean = !isEnabled
@@ -87,16 +87,13 @@
class PulseDisabledSuppressor(
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val userTracker: UserTracker,
-) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") {
+) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse disabled by user setting") {
override fun shouldSuppress(): Boolean =
!ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId)
}
class PulseBatterySaverSuppressor(private val batteryController: BatteryController) :
- VisualInterruptionCondition(
- types = setOf(PULSE),
- reason = "pulsing disabled by battery saver"
- ) {
+ VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse disabled by battery saver") {
override fun shouldSuppress() = batteryController.isAodPowerSave()
}
@@ -128,14 +125,14 @@
}
class PeekNotImportantSuppressor() :
- VisualInterruptionFilter(types = setOf(PEEK), reason = "not important") {
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "importance < HIGH") {
override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_HIGH
}
class PeekDeviceNotInUseSuppressor(
private val powerManager: PowerManager,
private val statusBarStateController: StatusBarStateController
-) : VisualInterruptionCondition(types = setOf(PEEK), reason = "not in use") {
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "device not in use") {
override fun shouldSuppress() =
when {
!powerManager.isScreenOn || statusBarStateController.isDreaming -> true
@@ -144,7 +141,7 @@
}
class PeekOldWhenSuppressor(private val systemClock: SystemClock) :
- VisualInterruptionFilter(types = setOf(PEEK), reason = "old when") {
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "has old `when`") {
private fun whenAge(entry: NotificationEntry) =
systemClock.currentTimeMillis() - entry.sbn.notification.`when`
@@ -165,21 +162,21 @@
}
class PulseEffectSuppressor() :
- VisualInterruptionFilter(types = setOf(PULSE), reason = "ambient effect suppressed") {
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "suppressed by DND") {
override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressAmbient()
}
class PulseLockscreenVisibilityPrivateSuppressor() :
VisualInterruptionFilter(
types = setOf(PULSE),
- reason = "notification hidden on lock screen by override"
+ reason = "hidden by lockscreen visibility override"
) {
override fun shouldSuppress(entry: NotificationEntry) =
entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE
}
class PulseLowImportanceSuppressor() :
- VisualInterruptionFilter(types = setOf(PULSE), reason = "importance less than DEFAULT") {
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "importance < DEFAULT") {
override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_DEFAULT
}
@@ -198,12 +195,12 @@
}
class BubbleNotAllowedSuppressor() :
- VisualInterruptionFilter(types = setOf(BUBBLE), reason = "not allowed") {
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "cannot bubble") {
override fun shouldSuppress(entry: NotificationEntry) = !entry.canBubble()
}
class BubbleNoMetadataSuppressor() :
- VisualInterruptionFilter(types = setOf(BUBBLE), reason = "no bubble metadata") {
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "has no or invalid bubble metadata") {
private fun isValidMetadata(metadata: BubbleMetadata?) =
metadata != null && (metadata.intent != null || metadata.shortcutId != null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
index 6af2543..b44a367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
@@ -50,19 +50,32 @@
val shouldFsi: Boolean
val wouldFsiWithoutDnd: Boolean
val logReason: String
+ val shouldLog: Boolean
+ val isWarning: Boolean
}
private enum class DecisionImpl(
override val shouldFsi: Boolean,
override val logReason: String,
override val wouldFsiWithoutDnd: Boolean = shouldFsi,
- val supersedesDnd: Boolean = false
+ val supersedesDnd: Boolean = false,
+ override val shouldLog: Boolean = true,
+ override val isWarning: Boolean = false
) : Decision {
- NO_FSI_NO_FULL_SCREEN_INTENT(false, "no full-screen intent", supersedesDnd = true),
+ NO_FSI_NO_FULL_SCREEN_INTENT(
+ false,
+ "no full-screen intent",
+ supersedesDnd = true,
+ shouldLog = false
+ ),
NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true),
NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"),
- NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(false, "suppressive group alert behavior"),
- NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false, "suppressive bubble metadata"),
+ NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(
+ false,
+ "suppressive group alert behavior",
+ isWarning = true
+ ),
+ NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false, "suppressive bubble metadata", isWarning = true),
NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"),
FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"),
FSI_DEVICE_DREAMING(true, "device is dreaming"),
@@ -71,7 +84,7 @@
FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"),
FSI_LOCKED_SHADE(true, "locked shade"),
FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"),
- NO_FSI_NO_HUN_OR_KEYGUARD(false, "no HUN or keyguard"),
+ NO_FSI_NO_HUN_OR_KEYGUARD(false, "no HUN or keyguard", isWarning = true),
NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false),
NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
new file mode 100644
index 0000000..1470b03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationInterruptLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.logKey
+import javax.inject.Inject
+
+class VisualInterruptionDecisionLogger
+@Inject
+constructor(@NotificationInterruptLog val buffer: LogBuffer) {
+ fun logHeadsUpFeatureChanged(isEnabled: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ { bool1 = isEnabled },
+ { "HUN feature is now ${if (bool1) "enabled" else "disabled"}" }
+ )
+ }
+
+ fun logWillDismissAll() {
+ buffer.log(TAG, INFO, {}, { "dismissing all HUNs since feature was disabled" })
+ }
+
+ fun logDecision(
+ type: String,
+ entry: NotificationEntry,
+ decision: VisualInterruptionDecisionProvider.Decision
+ ) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = type
+ bool1 = decision.shouldInterrupt
+ str2 = decision.logReason
+ str3 = entry.logKey
+ },
+ {
+ val outcome = if (bool1) "allowed" else "suppressed"
+ "$str1 $outcome: $str2 (key=$str3)"
+ }
+ )
+ }
+
+ fun logFullScreenIntentDecision(
+ entry: NotificationEntry,
+ decision: FullScreenIntentDecision,
+ warning: Boolean
+ ) {
+ buffer.log(
+ TAG,
+ if (warning) WARNING else DEBUG,
+ {
+ bool1 = decision.shouldInterrupt
+ bool2 = decision.wouldInterruptWithoutDnd
+ str1 = decision.logReason
+ str2 = entry.logKey
+ },
+ {
+ val outcome =
+ when {
+ bool1 -> "allowed"
+ bool2 -> "suppressed only by DND"
+ else -> "suppressed"
+ }
+ "FSI $outcome: $str1 (key=$str2)"
+ }
+ )
+ }
+}
+
+private const val TAG = "VisualInterruptionDecisionProvider"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 9640682..c0a1a32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -18,6 +18,7 @@
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
import android.os.PowerManager
+import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -46,7 +47,7 @@
private val headsUpManager: HeadsUpManager,
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
keyguardStateController: KeyguardStateController,
- private val logger: NotificationInterruptLogger,
+ private val logger: VisualInterruptionDecisionLogger,
@Main private val mainHandler: Handler,
private val powerManager: PowerManager,
private val statusBarStateController: StatusBarStateController,
@@ -58,9 +59,32 @@
override val logReason: String
) : Decision
+ private data class LoggableDecision private constructor(val decision: DecisionImpl) {
+ companion object {
+ val unsuppressed =
+ LoggableDecision(DecisionImpl(shouldInterrupt = true, logReason = "not suppressed"))
+
+ fun suppressed(legacySuppressor: NotificationInterruptSuppressor, methodName: String) =
+ LoggableDecision(
+ DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${legacySuppressor.name}.$methodName"
+ )
+ )
+
+ fun suppressed(suppressor: VisualInterruptionSuppressor) =
+ LoggableDecision(
+ DecisionImpl(shouldInterrupt = false, logReason = suppressor.reason)
+ )
+ }
+ }
+
private class FullScreenIntentDecisionImpl(
+ val entry: NotificationEntry,
private val fsiDecision: FullScreenIntentDecisionProvider.Decision
) : FullScreenIntentDecision {
+ var hasBeenLogged = false
+
override val shouldInterrupt
get() = fsiDecision.shouldFsi
@@ -69,6 +93,12 @@
override val logReason
get() = fsiDecision.logReason
+
+ val shouldLog
+ get() = fsiDecision.shouldLog
+
+ val isWarning
+ get() = fsiDecision.isWarning
}
private val fullScreenIntentDecisionProvider =
@@ -139,137 +169,113 @@
override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision {
check(started)
- return makeHeadsUpDecision(entry)
+
+ return if (statusBarStateController.isDozing) {
+ makeLoggablePulseDecision(entry)
+ } else {
+ makeLoggablePeekDecision(entry)
+ }
+ .decision
}
override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision {
check(started)
- return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) }
+
+ return if (statusBarStateController.isDozing) {
+ makeLoggablePulseDecision(entry).also { logDecision(PULSE, entry, it) }
+ } else {
+ makeLoggablePeekDecision(entry).also { logDecision(PEEK, entry, it) }
+ }
+ .decision
}
+ private fun makeLoggablePeekDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(PEEK)
+ ?: checkFilters(PEEK, entry) ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry) ?: checkSuppressAwakeHeadsUp(entry)
+ ?: LoggableDecision.unsuppressed
+
+ private fun makeLoggablePulseDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(PULSE)
+ ?: checkFilters(PULSE, entry) ?: checkSuppressInterruptions(entry)
+ ?: LoggableDecision.unsuppressed
+
override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision {
check(started)
- return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) }
+
+ return makeLoggableBubbleDecision(entry).also { logDecision(BUBBLE, entry, it) }.decision
+ }
+
+ private fun makeLoggableBubbleDecision(entry: NotificationEntry): LoggableDecision =
+ checkConditions(BUBBLE)
+ ?: checkFilters(BUBBLE, entry) ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry) ?: LoggableDecision.unsuppressed
+
+ private fun logDecision(
+ type: VisualInterruptionType,
+ entry: NotificationEntry,
+ loggable: LoggableDecision
+ ) {
+ logger.logDecision(type.name, entry, loggable.decision)
}
override fun makeUnloggedFullScreenIntentDecision(
entry: NotificationEntry
): FullScreenIntentDecision {
check(started)
- return makeFullScreenIntentDecision(entry)
+
+ val couldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt
+ val fsiDecision =
+ fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, couldHeadsUp)
+ return FullScreenIntentDecisionImpl(entry, fsiDecision)
}
override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
check(started)
- // Not yet implemented.
+
+ if (decision !is FullScreenIntentDecisionImpl) {
+ Log.wtf(TAG, "FSI decision $decision was not created by this class")
+ return
+ }
+
+ if (decision.hasBeenLogged) {
+ Log.wtf(TAG, "FSI decision $decision has already been logged")
+ return
+ }
+
+ decision.hasBeenLogged = true
+
+ if (!decision.shouldLog) {
+ return
+ }
+
+ logger.logFullScreenIntentDecision(decision.entry, decision, decision.isWarning)
}
- private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl {
- if (statusBarStateController.isDozing) {
- return makePulseDecision(entry)
- } else {
- return makePeekDecision(entry)
- }
- }
+ private fun checkSuppressInterruptions(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressInterruptions(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressInterruptions") }
- private fun makePeekDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(PEEK)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(PEEK, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- checkAwakeSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeInterruptions"
- )
- }
- checkAwakeHeadsUpSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeHeadsUpInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
- }
+ private fun checkSuppressAwakeInterruptions(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressAwakeInterruptions(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressAwakeInterruptions") }
- private fun makePulseDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(PULSE)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(PULSE, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
- }
+ private fun checkSuppressAwakeHeadsUp(entry: NotificationEntry) =
+ legacySuppressors
+ .firstOrNull { it.suppressAwakeHeadsUp(entry) }
+ ?.let { LoggableDecision.suppressed(it, "suppressAwakeHeadsUp") }
- private fun makeBubbleDecision(entry: NotificationEntry): DecisionImpl {
- checkConditions(BUBBLE)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkFilters(BUBBLE, entry)?.let {
- return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
- }
- checkSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressInterruptions"
- )
- }
- checkAwakeSuppressors(entry)?.let {
- return DecisionImpl(
- shouldInterrupt = false,
- logReason = "${it.name}.suppressAwakeInterruptions"
- )
- }
- return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
- }
+ private fun checkConditions(type: VisualInterruptionType) =
+ conditions
+ .firstOrNull { it.types.contains(type) && it.shouldSuppress() }
+ ?.let { LoggableDecision.suppressed(it) }
- private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) {
- // Not yet implemented.
- }
-
- private fun logBubbleDecision(entry: NotificationEntry, decision: DecisionImpl) {
- // Not yet implemented.
- }
-
- private fun makeFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision {
- val wouldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt
- val fsiDecision =
- fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, wouldHeadsUp)
- return FullScreenIntentDecisionImpl(fsiDecision)
- }
-
- private fun checkSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressInterruptions(entry) }
-
- private fun checkAwakeSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressAwakeInterruptions(entry) }
-
- private fun checkAwakeHeadsUpSuppressors(entry: NotificationEntry) =
- legacySuppressors.firstOrNull { it.suppressAwakeHeadsUp(entry) }
-
- private fun checkConditions(type: VisualInterruptionType): VisualInterruptionCondition? =
- conditions.firstOrNull { it.types.contains(type) && it.shouldSuppress() }
-
- private fun checkFilters(
- type: VisualInterruptionType,
- entry: NotificationEntry
- ): VisualInterruptionFilter? =
- filters.firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) }
+ private fun checkFilters(type: VisualInterruptionType, entry: NotificationEntry) =
+ filters
+ .firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) }
+ ?.let { LoggableDecision.suppressed(it) }
}
private const val TAG = "VisualInterruptionDecisionProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 7667e17..5cdead4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
@@ -40,6 +41,7 @@
configuration: ConfigurationState,
configurationController: ConfigurationController,
falsingManager: FalsingManager,
+ iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
notificationIconAreaController: NotificationIconAreaController,
shelfIconViewStore: ShelfNotificationIconViewStore,
) {
@@ -51,6 +53,7 @@
viewModel.icons,
configuration,
configurationController,
+ iconViewBindingFailureTracker,
shelfIconViewStore,
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 6cf5610..a5b87f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -45,6 +46,7 @@
private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
+ private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val shelfIconViewStore: ShelfNotificationIconViewStore,
) {
@@ -68,6 +70,7 @@
configuration,
configurationController,
falsingManager,
+ iconViewBindingFailureTracker,
iconAreaController,
shelfIconViewStore,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt
new file mode 100644
index 0000000..bcf7322
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/StatusBarNotificationViewBinderModule.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.ui.viewbinder
+
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import dagger.Module
+
+@Module(includes = [StatusBarIconViewBindingFailureTracker.Module::class])
+object StatusBarNotificationViewBinderModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index beeee1b..495b4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -252,7 +252,7 @@
}
if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
- newEntry == null ? null : newEntry.getKey());
+ newEntry == null ? null : newEntry.getRepresentativeEntry().getKey());
} else {
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 3921e69..7adc08c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -56,6 +56,7 @@
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
@@ -217,6 +218,7 @@
mWaitingForWindowStateChangeAfterCameraLaunch = false;
mTransitionFromLockscreenToDreamStarted = false;
};
+ private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
@Inject
public CollapsedStatusBarFragment(
@@ -235,6 +237,7 @@
KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController,
StatusBarStateController statusBarStateController,
+ StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
CommandQueue commandQueue,
CarrierConfigTracker carrierConfigTracker,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
@@ -264,6 +267,7 @@
mKeyguardStateController = keyguardStateController;
mShadeViewController = shadeViewController;
mStatusBarStateController = statusBarStateController;
+ mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
mCommandQueue = commandQueue;
mCarrierConfigTracker = carrierConfigTracker;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
@@ -471,6 +475,7 @@
mStatusBarIconsViewModel,
mConfigurationState,
mConfigurationController,
+ mIconViewBindingFailureTracker,
mStatusBarIconViewStore);
} else {
mNotificationIconAreaInner =
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt
similarity index 61%
copy from services/core/java/com/android/server/pm/pkg/component/ParsedService.java
copy to packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt
index 5fc251c..4cbdd6f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/StatusBarViewBinderModule.kt
@@ -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,16 +14,9 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.component;
+package com.android.systemui.statusbar.ui.binder
-import android.annotation.Nullable;
+import com.android.systemui.statusbar.notification.ui.viewbinder.StatusBarNotificationViewBinderModule
+import dagger.Module
-/** @hide **/
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface ParsedService extends ParsedMainComponent {
-
- int getForegroundServiceType();
-
- @Nullable
- String getPermission();
-}
+@Module(includes = [StatusBarNotificationViewBinderModule::class]) object StatusBarViewBinderModule
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 84d2b37..404621d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -34,7 +34,6 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -83,7 +82,6 @@
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -120,7 +118,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -304,7 +301,6 @@
private final DevicePostureController mDevicePostureController;
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
- private final FeatureFlags mFeatureFlags;
private final Lazy<SecureSettings> mSecureSettings;
private int mDialogTimeoutMillis;
@@ -323,9 +319,7 @@
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
- mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mHandler = new H(looper);
@@ -1373,14 +1367,12 @@
private void provideTouchFeedbackH(int newRingerMode) {
VibrationEffect effect = null;
- int hapticConstant = HapticFeedbackConstants.NO_HAPTICS;
switch (newRingerMode) {
case RINGER_MODE_NORMAL:
mController.scheduleTouchFeedback();
break;
case RINGER_MODE_SILENT:
effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_OFF;
break;
case RINGER_MODE_VIBRATE:
// Feedback handled by onStateChange, for feedback both when user toggles
@@ -1388,11 +1380,8 @@
break;
default:
effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_ON;
}
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mDialogView.performHapticFeedback(hapticConstant);
- } else if (effect != null) {
+ if (effect != null) {
mController.vibrate(effect);
}
}
@@ -1820,22 +1809,7 @@
&& mState.ringerModeInternal != -1
&& mState.ringerModeInternal != state.ringerModeInternal
&& state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
-
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- if (mShowing) {
- // The dialog view is responsible for triggering haptics in the oneway API
- mDialogView.performHapticFeedback(HapticFeedbackConstants.TOGGLE_ON);
- }
- /*
- TODO(b/290642122): If the dialog is not showing, we have the case where haptics is
- enabled by dragging the volume slider of Settings to a value of 0. This must be
- handled by view Slices in Settings whilst using the performHapticFeedback API.
- */
-
- } else {
- // Old behavior only active if the oneway API is not used.
- mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
- }
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
mState = state;
mDynamic.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index e3b3c21..53217d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -22,7 +22,6 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -65,7 +64,6 @@
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
@@ -82,7 +80,6 @@
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags,
secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 8154e04..6a08eea 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -190,6 +191,7 @@
mSmartspaceController,
mock(ConfigurationController.class),
mock(ScreenOffAnimationController.class),
+ mock(StatusBarIconViewBindingFailureTracker.class),
mKeyguardUnlockAnimationController,
mSecureSettings,
mExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 776799e..2b41e080 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -3436,7 +3436,8 @@
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
- when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1});
+ when(mUsbPortStatus.getComplianceWarnings())
+ .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
}
private Context getSpyContext() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index ae2ec2c..87ab5b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -143,6 +143,23 @@
assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true))
}
+ @Test
+ fun isPinEnhancedPrivacyEnabled() =
+ testScope.runTest {
+ whenever(lockPatternUtils.isPinEnhancedPrivacyEnabled(USER_INFOS[0].id))
+ .thenReturn(false)
+ whenever(lockPatternUtils.isPinEnhancedPrivacyEnabled(USER_INFOS[1].id))
+ .thenReturn(true)
+
+ val values by collectValues(underTest.isPinEnhancedPrivacyEnabled)
+ assertThat(values.first()).isTrue()
+ assertThat(values.last()).isFalse()
+
+ userRepository.setSelectedUserInfo(USER_INFOS[1])
+ assertThat(values.last()).isTrue()
+
+ }
+
private fun setSecurityModeAndDispatchBroadcast(
securityMode: KeyguardSecurityModel.SecurityMode,
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 6da6951..7a9cb6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -354,6 +354,18 @@
assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
+ @Test
+ fun isDigitButtonAnimationEnabled() =
+ testScope.runTest {
+ val isAnimationEnabled by collectLastValue(underTest.isDigitButtonAnimationEnabled)
+
+ utils.authenticationRepository.setPinEnhancedPrivacyEnabled(true)
+ assertThat(isAnimationEnabled).isFalse()
+
+ utils.authenticationRepository.setPinEnhancedPrivacyEnabled(false)
+ assertThat(isAnimationEnabled).isTrue()
+ }
+
private fun TestScope.lockDeviceAndOpenPinBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.deviceEntryRepository.setUnlocked(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index a6c4f19..af4bf36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -240,7 +240,7 @@
}
@Test
- fun contentOrdering() =
+ fun ordering_smartspaceBeforeUmoBeforeWidgets() =
testScope.runTest {
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
new file mode 100644
index 0000000..e8eda80
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryUdfpsInteractorTest : SysuiTestCase() {
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var biometricsRepository: FakeBiometricSettingsRepository
+
+ private lateinit var underTest: DeviceEntryUdfpsInteractor
+
+ @Before
+ fun setUp() {
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ biometricsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = fingerprintAuthRepository,
+ biometricSettingsRepository = biometricsRepository,
+ )
+ }
+
+ @Test
+ fun udfpsSupported_rearFp_false() = runTest {
+ val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+ fingerprintPropertyRepository.supportsRearFps()
+ assertThat(isUdfpsSupported).isFalse()
+ }
+
+ @Test
+ fun udfpsSupoprted() = runTest {
+ val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+ fingerprintPropertyRepository.supportsUdfps()
+ assertThat(isUdfpsSupported).isTrue()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ assertThat(isUdfpsEnrolledAndEnabled).isTrue()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled_rearFp_false() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+ }
+
+ @Test
+ fun udfpsEnrolledAndEnabled_notEnrolledOrEnabled_false() = runTest {
+ val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+ }
+
+ @Test
+ fun isListeningForUdfps() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsUdfps()
+ fingerprintAuthRepository.setIsRunning(true)
+ assertThat(isListeningForUdfps).isTrue()
+ }
+
+ @Test
+ fun isListeningForUdfps_rearFp_false() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsRearFps()
+ fingerprintAuthRepository.setIsRunning(true)
+ assertThat(isListeningForUdfps).isFalse()
+ }
+
+ @Test
+ fun isListeningForUdfps_notRunning_false() = runTest {
+ val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+ fingerprintPropertyRepository.supportsUdfps()
+ fingerprintAuthRepository.setIsRunning(false)
+ assertThat(isListeningForUdfps).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index 5eab2fc..df52265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -84,8 +84,8 @@
@Test
fun udfpsBurnInOffset_updatesOnResolutionScaleChange() =
testScope.runTest {
- val udfpsBurnInOffsetX by collectLastValue(underTest.udfpsBurnInXOffset)
- val udfpsBurnInOffsetY by collectLastValue(underTest.udfpsBurnInYOffset)
+ val udfpsBurnInOffsetX by collectLastValue(underTest.deviceEntryIconXOffset)
+ val udfpsBurnInOffsetY by collectLastValue(underTest.deviceEntryIconYOffset)
assertThat(udfpsBurnInOffsetX).isEqualTo(burnInOffset)
assertThat(udfpsBurnInOffsetY).isEqualTo(burnInOffset)
@@ -101,7 +101,7 @@
@Test
fun udfpsBurnInProgress_updatesOnDozeTimeTick() =
testScope.runTest {
- val udfpsBurnInProgress by collectLastValue(underTest.udfpsBurnInProgress)
+ val udfpsBurnInProgress by collectLastValue(underTest.udfpsProgress)
assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
setBurnInProgress(.88f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 3442df6..2dfc132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -123,30 +123,30 @@
runCurrent()
// THEN burn in offsets are 0
- assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
- assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
- assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
+ assertThat(burnInOffsets?.progress).isEqualTo(0f)
+ assertThat(burnInOffsets?.y).isEqualTo(0)
+ assertThat(burnInOffsets?.x).isEqualTo(0)
// WHEN we're in the middle of the doze amount change
keyguardRepository.setDozeAmount(.50f)
runCurrent()
// THEN burn in is updated (between 0 and the full offset)
- assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
- assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
- assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
- assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
- assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
- assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
+ assertThat(burnInOffsets?.progress).isGreaterThan(0f)
+ assertThat(burnInOffsets?.y).isGreaterThan(0)
+ assertThat(burnInOffsets?.x).isGreaterThan(0)
+ assertThat(burnInOffsets?.progress).isLessThan(burnInProgress)
+ assertThat(burnInOffsets?.y).isLessThan(burnInYOffset)
+ assertThat(burnInOffsets?.x).isLessThan(burnInXOffset)
// WHEN we're fully dozing
keyguardRepository.setDozeAmount(1f)
runCurrent()
// THEN burn in offsets are updated to final current values (for the given time)
- assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
- assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
- assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
+ assertThat(burnInOffsets?.progress).isEqualTo(burnInProgress)
+ assertThat(burnInOffsets?.y).isEqualTo(burnInYOffset)
+ assertThat(burnInOffsets?.x).isEqualTo(burnInXOffset)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 71313c8..75bdcdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -24,12 +24,14 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
@@ -42,6 +44,7 @@
import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
@@ -77,7 +80,9 @@
notificationPanelView,
featureFlags,
{ lockIconViewController },
- { DeviceEntryIconViewModel() },
+ { mock(DeviceEntryIconViewModel::class.java) },
+ { mock(DeviceEntryForegroundViewModel::class.java) },
+ { mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..f282481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToGoneTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToGoneTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest = AodToGoneTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..517149c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ underTest =
+ AodToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewShows() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_udfps_alphaFadeIn() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // fade in
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.2f)
+
+ repository.sendTransitionStep(step(0.3f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.6f)
+
+ repository.sendTransitionStep(step(0.6f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_rearFp_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.3f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(0.6f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..96f69462
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToOccludedTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: AodToOccludedTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest = AodToOccludedTransitionViewModel(interactor)
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { Truth.assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.OCCLUDED,
+ value = value,
+ transitionState = state,
+ ownerName = "AodToOccludedTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..5dccc3b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var testScope: TestScope
+ private lateinit var underTest: DozingToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ underTest =
+ DozingToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewShows() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "DozingToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index 6d47aed..fd125e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -35,6 +41,7 @@
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -44,22 +51,34 @@
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
val interactor =
KeyguardTransitionInteractorFactory.create(
scope = TestScope().backgroundScope,
repository = repository,
)
.keyguardTransitionInteractor
- underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
+ underTest =
+ DreamingToLockscreenTransitionViewModel(
+ interactor,
+ mock(),
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = FakeBiometricSettingsRepository(),
+ ),
+ )
}
@Test
@@ -129,6 +148,78 @@
}
@Test
+ fun deviceEntryParentViewFadeIn() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAppear() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+ sensorLocations = emptyMap(),
+ )
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ values.forEach { assertThat(it).isEqualTo(1f) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackground_noUdfps_noUpdates() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.REAR,
+ sensorLocations = emptyMap(),
+ )
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, STARTED))
+ repository.sendTransitionStep(step(0f))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.2f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(0) // no updates
+
+ job.cancel()
+ }
+
+ @Test
fun lockscreenTranslationY() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index 255f4df..c1444a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -19,7 +19,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -27,6 +31,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.collect.Range
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.runTest
@@ -34,11 +39,14 @@
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class GoneToAodTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: GoneToAodTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var testScope: TestScope
@Before
@@ -47,13 +55,24 @@
testScope = TestScope(testDispatcher)
repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = GoneToAodTransitionViewModel(interactor)
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ GoneToAodTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
}
@Test
@@ -63,11 +82,11 @@
val enterFromTopTranslationY by
collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
- // The animation should only start > halfway through
+ // The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(enterFromTopTranslationY).isEqualTo(pixels)
- repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.4f))
assertThat(enterFromTopTranslationY).isEqualTo(pixels)
repository.sendTransitionStep(step(.85f))
@@ -83,11 +102,11 @@
testScope.runTest {
val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
- // The animation should only start > halfway through
+ // The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
- repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.4f))
assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
repository.sendTransitionStep(step(.85f))
@@ -97,6 +116,98 @@
assertThat(enterFromTopAnimationAlpha).isEqualTo(1f)
}
+ @Test
+ fun deviceEntryBackgroundViewAlpha() =
+ testScope.runTest {
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.01f, 1f))
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.99f, 1f))
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() =
+ testScope.runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..4074851
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToAodTransitionViewModelTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToAodTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val deviceEntryRepository: FakeDeviceEntryRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+ val fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ val biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+
+ fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerLockscreenToAodTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(FACE_AUTH_REFACTOR, true)
+ set(FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+
+ @Test
+ fun backgroundViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading out before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun backgroundViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeNotExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ shadeExpanded(false)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.3f),
+ step(.7f),
+ step(1f),
+ ),
+ testScope = testScope,
+ )
+ // immediately 1f
+ values.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ shadeExpanded(true)
+ runCurrent()
+
+ // fade in
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading in before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFp_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsRearFps()
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+ // finish fading out before the end of the full transition
+ repository.sendTransitionStep(step(.7f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFp_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ fingerprintPropertyRepository.supportsRearFps()
+ shadeExpanded(true)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.3f),
+ step(.7f),
+ step(1f),
+ ),
+ testScope = testScope,
+ )
+ // immediately 0f
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 89a1d2b..5c85357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,88 +18,172 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import dagger.BindsInstance
+import dagger.Component
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
- private lateinit var underTest: LockscreenToDreamingTransitionViewModel
- private lateinit var repository: FakeKeyguardTransitionRepository
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToDreamingTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
- @Before
- fun setUp() {
- repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = LockscreenToDreamingTransitionViewModel(interactor)
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+
+ fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
}
+ private val testComponent: TestComponent =
+ DaggerLockscreenToDreamingTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
@Test
fun lockscreenFadeOut() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
- val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
-
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.1f))
- repository.sendTransitionStep(step(0.2f))
- repository.sendTransitionStep(step(0.3f))
- // ...up to here
- repository.sendTransitionStep(step(1f))
+ testComponent.runTest {
+ val values by collectValues(underTest.lockscreenAlpha)
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.1f),
+ step(.2f),
+ step(.3f), // ...up to here
+ step(1f),
+ ),
+ testScope = testScope,
+ )
// Only three values should be present, since the dream overlay runs for a small
// fraction of the overall animation time
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationY() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.5f))
- repository.sendTransitionStep(step(1f))
- // And a final reset event on FINISHED
- repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f),
+ step(1f, TransitionState.FINISHED), // Final reset event on FINISHED
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(6)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
// Validate finished value
assertThat(values[5]).isEqualTo(0f)
+ }
- job.cancel()
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f),
+ step(1f, TransitionState.FINISHED),
+ ),
+ testScope = testScope,
+ )
+
+ // immediately 0f
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
}
private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..1494c92
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToGoneTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: LockscreenToGoneTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest =
+ LockscreenToGoneTransitionViewModel(
+ interactor,
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewHides() = runTest {
+ val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+ deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 41f8856..4cbefa3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,109 +18,185 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import dagger.BindsInstance
+import dagger.Component
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
- private lateinit var underTest: LockscreenToOccludedTransitionViewModel
- private lateinit var repository: FakeKeyguardTransitionRepository
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToOccludedTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
- @Before
- fun setUp() {
- repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = LockscreenToOccludedTransitionViewModel(interactor)
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+
+ fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
}
+ private val testComponent: TestComponent =
+ DaggerLockscreenToOccludedTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+
@Test
fun lockscreenFadeOut() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
- val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
-
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.1f))
- repository.sendTransitionStep(step(0.4f))
- repository.sendTransitionStep(step(0.7f))
- // ...up to here
- repository.sendTransitionStep(step(1f))
-
+ testComponent.runTest {
+ val values by collectValues(underTest.lockscreenAlpha)
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.1f),
+ step(.4f),
+ step(.7f), // ...up to here
+ step(1f),
+ ),
+ testScope = testScope,
+ )
// Only 3 values should be present, since the dream overlay runs for a small fraction
// of the overall animation time
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationY() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
- // Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.5f))
- repository.sendTransitionStep(step(1f))
- // ...up to here
-
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED), // Should start running here...
+ step(0f),
+ step(.3f),
+ step(.5f),
+ step(1f), // ...up to here
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
-
- job.cancel()
}
@Test
fun lockscreenTranslationYIsCanceled() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<Float>()
-
+ testComponent.runTest {
val pixels = 100
- val job =
- underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- repository.sendTransitionStep(step(0f))
- repository.sendTransitionStep(step(0.3f))
- repository.sendTransitionStep(step(0.3f, TransitionState.CANCELED))
-
+ val values by collectValues(underTest.lockscreenTranslationY(pixels))
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(.3f),
+ step(0.3f, TransitionState.CANCELED),
+ ),
+ testScope = testScope,
+ )
assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
// Cancel will reset the translation
assertThat(values[3]).isEqualTo(0)
+ }
- job.cancel()
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionSteps(
+ steps =
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(.5f),
+ step(1f, TransitionState.FINISHED)
+ ),
+ testScope = testScope,
+ )
+
+ values.forEach { assertThat(it).isEqualTo(0f) }
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.2f))
+ assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(actual).isEqualTo(0f)
}
private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
new file mode 100644
index 0000000..4f56435
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent : SysUITestComponent<LockscreenToPrimaryBouncerTransitionViewModel> {
+ val repository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val shadeRepository: FakeShadeRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+
+ fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
+ }
+
+ private val testComponent: TestComponent =
+ DaggerLockscreenToPrimaryBouncerTransitionViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks = TestMocksModule(),
+ )
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(true)
+ runCurrent()
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.2f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.8f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+ testComponent.runTest {
+ val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ shadeExpanded(false)
+ runCurrent()
+
+ // fade out
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.1f))
+ runCurrent()
+ Truth.assertThat(actual).isIn(Range.open(.1f, .9f))
+
+ // alpha is 1f before the full transition starts ending
+ repository.sendTransitionStep(step(0.8f))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ runCurrent()
+ Truth.assertThat(actual).isEqualTo(0f)
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING,
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = state,
+ ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..0eb8ff6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludedToAodTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: OccludedToAodTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ OccludedToAodTransitionViewModel(
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha() = runTest {
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // no updates
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "OccludedToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index ec95cb8..d077227 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -26,6 +30,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -35,22 +40,35 @@
import org.junit.Test
import org.junit.runner.RunWith
+@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: OccludedToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor
- underTest = OccludedToLockscreenTransitionViewModel(interactor)
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ underTest =
+ OccludedToLockscreenTransitionViewModel(
+ interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor =
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
}
@Test
@@ -113,6 +131,78 @@
job.cancel()
}
+ @Test
+ fun deviceEntryParentViewFadeIn() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ // Should start running here...
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ // ...up to here
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(5)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewShows() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ values.forEach { assertThat(it).isEqualTo(1f) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun deviceEntryBackgroundView_noUdfpsEnrolled_noUpdates() =
+ runTest(UnconfinedTestDispatcher()) {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val values = mutableListOf<Float>()
+
+ val job =
+ underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.4f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(0.8f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values).isEmpty() // no updates
+
+ job.cancel()
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..350b310
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -0,0 +1,164 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: PrimaryBouncerToAodTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
+ underTest =
+ PrimaryBouncerToAodTransitionViewModel(
+ interactor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val deviceEntryBackgroundViewAlpha by
+ collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 0f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsEnrolled_fadeIn() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(.75f))
+ repository.sendTransitionStep(step(1f))
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // animation doesn't start until the end
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.95f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.75f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(deviceEntryParentViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.AOD,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToAodTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..24e4920
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: PrimaryBouncerToLockscreenTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ underTest =
+ PrimaryBouncerToLockscreenTransitionViewModel(
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor,
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+ biometricSettingsRepository = biometricSettingsRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun deviceEntryParentViewAlpha() = runTest {
+ val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.4f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() = runTest {
+ fingerprintPropertyRepository.supportsUdfps()
+ val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+ // immediately 1f
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(0.1f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.3f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(.5f))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(bgViewAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun deviceEntryBackgroundViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+ fingerprintPropertyRepository.supportsRearFps()
+ val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(.75f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(bgViewAlpha).isNull()
+
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(bgViewAlpha).isNull()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index b101acf..437a35f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -38,8 +38,6 @@
import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
@@ -112,7 +110,6 @@
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
- private val featureFlags = FakeFeatureFlagsClassic()
@Before
fun setUp() {
@@ -131,7 +128,6 @@
fakeFgExecutor,
fakeBgExecutor,
dumpster,
- featureFlags,
)
manager.addListener(listener)
@@ -150,7 +146,6 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, false)
}
@After
@@ -463,7 +458,6 @@
@Test
fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session name is null, and is a system session for a PhoneMediaDevice
val phoneDevice = mock(PhoneMediaDevice::class.java)
whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
@@ -489,7 +483,6 @@
@Test
fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session does not have a name, and is a system session
whenever(route.name).thenReturn(null)
whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
@@ -725,101 +718,6 @@
assertThat(data.showBroadcastButton).isFalse()
}
- // Duplicates of above tests with MEDIA_DEVICE_NAME_FIX enabled
-
- @Test
- fun loadMediaDataWithNullToken_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- }
-
- @Test
- fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData)
- // Run and reset the executors and listeners so we only focus on new events.
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- reset(listener)
-
- // Ensure we'll get device info when using the address
- val fullMediaDevice = mock(MediaDevice::class.java)
- val address = "fakeAddress"
- val nameFromDevice = "nameFromDevice"
- val iconFromDevice = mock(Drawable::class.java)
- whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
- whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
- whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
-
- // WHEN the about-to-connect device changes to non-null
- val deviceCallback = captureCallback()
- val nameFromParam = "nameFromParam"
- val iconFromParam = mock(Drawable::class.java)
- deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
-
- // THEN the about-to-connect device based on the address is returned
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(nameFromDevice)
- assertThat(data.name).isNotEqualTo(nameFromParam)
- assertThat(data.icon).isEqualTo(iconFromDevice)
- assertThat(data.icon).isNotEqualTo(iconFromParam)
- }
-
- @Test
- fun deviceNameFromMR2RouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a valid routing session
- whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN it uses the route name (instead of device name)
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
- }
-
- @Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns null for routing session
- whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is disabled and name is set to null
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- }
-
- @Test
- fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a routing session that does not have a name
- whenever(route.name).thenReturn(null)
- whenever(route.isSystemSession).thenReturn(false)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is enabled and uses the current connected device name
- val data = captureDeviceData(KEY)
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- assertThat(data.enabled).isTrue()
- }
-
- // End duplicate tests
-
private fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 3b6bfee..3808c7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -32,6 +32,11 @@
import com.android.systemui.res.R
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -67,17 +72,24 @@
private val fakeSystemClock = FakeSystemClock()
+ private lateinit var scheduler: TestCoroutineScheduler
+ private lateinit var dispatcher: CoroutineDispatcher
+ private lateinit var testScope: TestScope
private lateinit var icon: Pair<Drawable, String>
private lateinit var bluetoothTileDialog: BluetoothTileDialog
private lateinit var deviceItem: DeviceItem
@Before
fun setUp() {
+ scheduler = TestCoroutineScheduler()
+ dispatcher = UnconfinedTestDispatcher(scheduler)
+ testScope = TestScope(dispatcher)
bluetoothTileDialog =
BluetoothTileDialog(
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -111,29 +123,33 @@
@Test
fun testShowDialog_displayBluetoothDevice() {
- bluetoothTileDialog =
- BluetoothTileDialog(
- ENABLED,
- subtitleResId,
- bluetoothTileDialogCallback,
- fakeSystemClock,
- uiEventLogger,
- logger,
- mContext
+ testScope.runTest {
+ bluetoothTileDialog =
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ dispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ mContext
+ )
+ bluetoothTileDialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = false
)
- bluetoothTileDialog.show()
- bluetoothTileDialog.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = false
- )
- val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(adapter.itemCount).isEqualTo(1)
- assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
- assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
- assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+ assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME)
+ assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY)
+ assertThat(adapter.getItem(0).iconWithDescription).isEqualTo(icon)
+ }
}
@Test
@@ -147,6 +163,7 @@
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -173,6 +190,7 @@
ENABLED,
subtitleResId,
bluetoothTileDialogCallback,
+ dispatcher,
fakeSystemClock,
uiEventLogger,
logger,
@@ -190,33 +208,37 @@
@Test
fun testOnDeviceUpdated_hideSeeAll_showPairNew() {
- bluetoothTileDialog =
- BluetoothTileDialog(
- ENABLED,
- subtitleResId,
- bluetoothTileDialogCallback,
- fakeSystemClock,
- uiEventLogger,
- logger,
- mContext
+ testScope.runTest {
+ bluetoothTileDialog =
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ dispatcher,
+ fakeSystemClock,
+ uiEventLogger,
+ logger,
+ mContext
+ )
+ bluetoothTileDialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ bluetoothTileDialog.onDeviceItemUpdated(
+ listOf(deviceItem),
+ showSeeAll = false,
+ showPairNewDevice = true
)
- bluetoothTileDialog.show()
- bluetoothTileDialog.onDeviceItemUpdated(
- listOf(deviceItem),
- showSeeAll = false,
- showPairNewDevice = true
- )
- val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
- val pairNewLayout =
- bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
- val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
- val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
+ val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
+ val pairNewLayout =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
+ val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(seeAllLayout).isNotNull()
- assertThat(seeAllLayout.visibility).isEqualTo(GONE)
- assertThat(pairNewLayout).isNotNull()
- assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
- assertThat(adapter.itemCount).isEqualTo(1)
+ assertThat(seeAllLayout).isNotNull()
+ assertThat(seeAllLayout.visibility).isEqualTo(GONE)
+ assertThat(pairNewLayout).isNotNull()
+ assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(adapter.itemCount).isEqualTo(1)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
index 682b2d0..5eca8ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
@@ -19,7 +19,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,8 +31,8 @@
class QSTileConfigProviderTest : SysuiTestCase() {
private val underTest =
- QSTileConfigProviderImpl(
- mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
+ createQSTileConfigProviderImpl(
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC }),
)
@Test
@@ -43,13 +45,31 @@
underTest.getConfig(INVALID_SPEC.spec)
}
+ @Test
+ fun hasConfigReturnsTrueForValidSpec() {
+ assertThat(underTest.hasConfig(VALID_SPEC.spec)).isTrue()
+ }
+
+ @Test
+ fun hasConfigReturnsFalseForInvalidSpec() {
+ assertThat(underTest.hasConfig(INVALID_SPEC.spec)).isFalse()
+ }
+
@Test(expected = IllegalArgumentException::class)
fun validatesSpecUponCreation() {
- QSTileConfigProviderImpl(
+ createQSTileConfigProviderImpl(
mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = INVALID_SPEC })
)
}
+ private fun createQSTileConfigProviderImpl(
+ configs: Map<String, QSTileConfig>
+ ): QSTileConfigProviderImpl =
+ QSTileConfigProviderImpl(
+ configs,
+ mock<QsEventLogger>(),
+ )
+
private companion object {
val VALID_SPEC = TileSpec.create("valid_tile_spec")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
deleted file mode 100644
index d3b7daa..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ /dev/null
@@ -1,120 +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.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.tiles.viewmodel
-
-import android.os.UserHandle
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-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
-
-// TODO(b/299909368): Add more tests
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
-
- @Mock private lateinit var qsTileLogger: QSTileLogger
- @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
-
- private val fakeUserRepository = FakeUserRepository()
- private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
- private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
- private val fakeDisabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
- private val fakeFalsingManager = FalsingManagerFake()
-
- private val testCoroutineDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testCoroutineDispatcher)
-
- private lateinit var underTest: QSTileViewModel
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- underTest = createViewModel(testScope)
- }
-
- @Test
- fun testDoesntListenStateUntilCreated() =
- testScope.runTest {
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- underTest.state.launchIn(backgroundScope)
- runCurrent()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
- assertThat(fakeQSTileDataInteractor.dataRequests.first())
- .isEqualTo(FakeQSTileDataInteractor.DataRequest(UserHandle.of(0)))
- }
-
- private fun createViewModel(
- scope: TestScope,
- config: QSTileConfig = TEST_QS_TILE_CONFIG,
- ): QSTileViewModel =
- QSTileViewModelImpl(
- config,
- { fakeQSTileUserActionInteractor },
- { fakeQSTileDataInteractor },
- {
- object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState =
- QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- ""
- ) {}
- }
- },
- fakeDisabledByPolicyInteractor,
- fakeUserRepository,
- fakeFalsingManager,
- qsTileAnalytics,
- qsTileLogger,
- FakeSystemClock(),
- testCoroutineDispatcher,
- scope.backgroundScope,
- )
-
- private companion object {
-
- val TEST_QS_TILE_CONFIG = QSTileConfigTestBuilder.build {}
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
new file mode 100644
index 0000000..3a0ebdb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.tiles.viewmodel
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+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.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun stateReceivedForTheData() =
+ testScope.runTest {
+ val testTileData = "test_tile_data"
+ val states = collectValues(underTest.state)
+ runCurrent()
+
+ tileDataInteractor.emitData(testTileData)
+ runCurrent()
+
+ assertThat(states()).isNotEmpty()
+ assertThat(states().first().label).isEqualTo(testTileData)
+ verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun doesntListenDataIfStateIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListenAvailabilityIfAvailabilityIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListedDataAfterDestroy() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.destroy()
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+ }
+
+ @Test
+ fun forceUpdateTriggersData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.forceUpdate()
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.ForceUpdate::class.java)
+ verify(qsTileLogger).logForceUpdate(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun userChangeUpdatesData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.DataRequest(USER))
+ }
+
+ @Test
+ fun userChangeUpdatesAvailability() =
+ testScope.runTest {
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilityRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
+ }
+
+ private fun createViewModel(
+ scope: TestScope,
+ config: QSTileConfig = tileConfig,
+ ): QSTileViewModel =
+ QSTileViewModelImpl(
+ config,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+
+ private companion object {
+
+ val USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
new file mode 100644
index 0000000..ea8acc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.tiles.viewmodel
+
+import androidx.test.filters.MediumTest
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+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.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/** Tests all possible [QSTileUserAction]s. If you need */
+@MediumTest
+@RunWith(Parameterized::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelUserInputTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ @Parameter lateinit var userAction: QSTileUserAction
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun userInputTriggersData() =
+ testScope.runTest {
+ tileDataInteractor.emitData("initial_data")
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserAction(eq(userAction), eq(tileConfig.tileSpec), eq(true), eq(true))
+ verify(qsTileLogger)
+ .logUserActionPipeline(
+ eq(tileConfig.tileSpec),
+ eq(userAction),
+ any(),
+ eq("initial_data")
+ )
+ verify(qsTileAnalytics).trackUserAction(eq(tileConfig), eq(userAction))
+ }
+
+ @Test
+ fun disabledByPolicyUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ disabledByPolicyInteractor.policyResult =
+ DisabledByPolicyInteractor.PolicyResult.TileDisabled(
+ RestrictedLockUtils.EnforcedAdmin()
+ )
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByPolicy(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun falsedUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ falsingManager.setFalseLongTap(true)
+ falsingManager.setFalseTap(true)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByFalsing(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun userInputIsThrottled() =
+ testScope.runTest {
+ val inputCount = 100
+ underTest.state.launchIn(backgroundScope)
+
+ repeat(inputCount) { underTest.onActionPerformed(userAction) }
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.size).isLessThan(inputCount)
+ }
+
+ private fun createViewModel(scope: TestScope): QSTileViewModel =
+ QSTileViewModelImpl(
+ tileConfig,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun data(): Iterable<QSTileUserAction> =
+ listOf(
+ QSTileUserAction.Click(null),
+ QSTileUserAction.LongClick(null),
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index b8fe2f9..cb83e7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -20,10 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.smartspace.config.BcSmartspaceConfigProvider
-import com.android.systemui.util.mockito.whenever
-import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -45,16 +42,7 @@
}
@Test
- fun isDefaultDateWeatherDisabled_flagIsTrue_returnsTrue() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
+ fun isDefaultDateWeatherDisabled_returnsTrue() {
assertTrue(configProvider.isDefaultDateWeatherDisabled)
}
-
- @Test
- fun isDefaultDateWeatherDisabled_flagIsFalse_returnsFalse() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- assertFalse(configProvider.isDefaultDateWeatherDisabled)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 9036f22..8440e00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
@@ -205,10 +204,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
- // tests.
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
`when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
@@ -260,17 +255,6 @@
deviceProvisionedListener = deviceProvisionedCaptor.value
}
- @Test(expected = RuntimeException::class)
- fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
- // GIVEN the feature flag is disabled
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- // WHEN we try to build the view
- controller.buildAndConnectWeatherView(fakeParent)
-
- // THEN an exception is thrown
- }
-
@Test
fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
// GIVEN an unprovisioned device and an attempt to connect
@@ -332,6 +316,8 @@
clearInvocations(plugin)
// WHEN the session is closed
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -376,20 +362,6 @@
configChangeListener.onThemeChanged()
// We update the new text color to match the wallpaper color
- verify(smartspaceView).setPrimaryTextColor(anyInt())
- }
-
- @Test
- fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the theme changes
- configChangeListener.onThemeChanged()
-
- // We update the new text color to match the wallpaper color
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setPrimaryTextColor(anyInt())
@@ -404,20 +376,6 @@
statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
// We pass that along to the view
- verify(smartspaceView).setDozeAmount(0.7f)
- }
-
- @Test
- fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the doze amount changes
- statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
-
- // We pass that along to the view
verify(dateSmartspaceView).setDozeAmount(0.7f)
verify(weatherSmartspaceView).setDozeAmount(0.7f)
verify(smartspaceView).setDozeAmount(0.7f)
@@ -472,7 +430,7 @@
}
@Test
- fun testAllTargetsAreFilteredExceptWeatherWhenNotificationsAreDisabled() {
+ fun testAllTargetsAreFilteredInclWeatherWhenNotificationsAreDisabled() {
// GIVEN the active user doesn't allow any notifications on lockscreen
setShowNotifications(userHandlePrimary, false)
connectSession()
@@ -488,7 +446,7 @@
sessionListener.onTargetsAvailable(targets)
// THEN all non-sensitive content is still shown
- verify(plugin).onTargetsAvailable(eq(listOf(targets[3])))
+ verify(plugin).onTargetsAvailable(emptyList())
}
@Test
@@ -519,8 +477,7 @@
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherTargetIsFilteredOut() {
connectSession()
// WHEN we receive a list of targets
@@ -670,8 +627,7 @@
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherDataUpdates() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
@@ -699,33 +655,6 @@
}
@Test
- fun testSessionListener_ifDecouplingDisabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
- connectSession()
-
- clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
- // WHEN we receive a list of targets
- val targets = listOf(
- makeWeatherTargetWithExtras(
- id = 1,
- userHandle = userHandlePrimary,
- description = "Sunny",
- state = WeatherData.WeatherStateIcon.SUNNY.id,
- temperature = "32",
- useCelsius = false),
- makeTarget(2, userHandlePrimary, isSensitive = true)
- )
-
- sessionListener.onTargetsAvailable(targets)
-
- verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
- w.description == "Sunny" &&
- w.state == WeatherData.WeatherStateIcon.SUNNY &&
- w.temperature == 32 && !w.useCelsius
- })
- }
-
- @Test
fun testSettingsAreReloaded() {
// GIVEN a connected session where the privacy settings later flip to false
connectSession()
@@ -781,6 +710,8 @@
connectSession()
// WHEN we are told to cleanup
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -816,16 +747,6 @@
}
@Test
- fun testWeatherViewUsesSameSession() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
- // GIVEN a connected session
- connectSession()
-
- // No checks is needed here, since connectSession() already checks internally that
- // createSmartspaceSession is invoked only once.
- }
-
- @Test
fun testViewGetInitializedWithBypassEnabledState() {
// GIVEN keyguard bypass is enabled.
`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -853,31 +774,29 @@
}
private fun connectSession() {
- if (controller.isDateWeatherDecoupled()) {
- val dateView = controller.buildAndConnectDateView(fakeParent)
- dateSmartspaceView = dateView as SmartspaceView
- fakeParent.addView(dateView)
- controller.stateChangeListener.onViewAttachedToWindow(dateView)
+ val dateView = controller.buildAndConnectDateView(fakeParent)
+ dateSmartspaceView = dateView as SmartspaceView
+ fakeParent.addView(dateView)
+ controller.stateChangeListener.onViewAttachedToWindow(dateView)
- verify(dateSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(dateSmartspaceView).registerDataProvider(datePlugin)
+ verify(dateSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(dateSmartspaceView).registerDataProvider(datePlugin)
- verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
- verify(dateSmartspaceView).setDozeAmount(0.5f)
+ verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(dateSmartspaceView).setDozeAmount(0.5f)
- val weatherView = controller.buildAndConnectWeatherView(fakeParent)
- weatherSmartspaceView = weatherView as SmartspaceView
- fakeParent.addView(weatherView)
- controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent)
+ weatherSmartspaceView = weatherView as SmartspaceView
+ fakeParent.addView(weatherView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
- verify(weatherSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
+ verify(weatherSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
- verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
- verify(weatherSmartspaceView).setDozeAmount(0.5f)
- }
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(weatherSmartspaceView).setDozeAmount(0.5f)
val view = controller.buildAndConnectView(fakeParent)
smartspaceView = view as SmartspaceView
@@ -918,10 +837,8 @@
verify(smartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setDozeAmount(0.5f)
- if (controller.isDateWeatherDecoupled()) {
- clearInvocations(dateSmartspaceView)
- clearInvocations(weatherSmartspaceView)
- }
+ clearInvocations(dateSmartspaceView)
+ clearInvocations(weatherSmartspaceView)
clearInvocations(smartspaceView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 1a04a3e..87e9735 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -389,4 +389,31 @@
assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
assertThat(isolatedIcon?.isAnimating).isFalse()
}
+
+ @Test
+ fun isolatedIcon_updateWhenIconDataChanges() =
+ testComponent.runTest {
+ val icon: Icon = mock()
+ val isolatedIcon by collectLastValue(underTest.isolatedIcon)
+ runCurrent()
+
+ headsUpViewStateRepository.isolatedNotification.value = "notif1"
+ runCurrent()
+
+ activeNotificationsRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ groupKey = "group",
+ statusBarIcon = icon
+ )
+ )
+ }
+ .build()
+ runCurrent()
+
+ assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 1d2055e..e1581ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -44,7 +44,7 @@
statusBarStateController,
keyguardStateController,
headsUpManager,
- logger,
+ oldLogger,
mainHandler,
flags,
keyguardNotificationVisibilityProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index 722b170..1064475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -37,7 +37,7 @@
headsUpManager,
keyguardNotificationVisibilityProvider,
keyguardStateController,
- logger,
+ newLogger,
mainHandler,
powerManager,
statusBarStateController,
@@ -222,14 +222,14 @@
private class TestCondition(
types: Set<VisualInterruptionType>,
val onShouldSuppress: () -> Boolean
- ) : VisualInterruptionCondition(types = types, reason = "") {
+ ) : VisualInterruptionCondition(types = types, reason = "test condition") {
override fun shouldSuppress(): Boolean = onShouldSuppress()
}
private class TestFilter(
types: Set<VisualInterruptionType>,
val onShouldSuppress: (NotificationEntry) -> Boolean = { true }
- ) : VisualInterruptionFilter(types = types, reason = "") {
+ ) : VisualInterruptionFilter(types = types, reason = "test filter") {
override fun shouldSuppress(entry: NotificationEntry) = onShouldSuppress(entry)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 0f29836..5e81156 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -20,7 +20,9 @@
import android.app.Notification
import android.app.Notification.BubbleMetadata
import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.FLAG_FOREGROUND_SERVICE
import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED
+import android.app.Notification.FLAG_USER_INITIATED_JOB
import android.app.Notification.GROUP_ALERT_ALL
import android.app.Notification.GROUP_ALERT_CHILDREN
import android.app.Notification.GROUP_ALERT_SUMMARY
@@ -47,6 +49,9 @@
import android.provider.Settings.Global.HEADS_UP_ON
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.FakeStatusBarStateController
@@ -76,19 +81,35 @@
import org.mockito.Mockito.`when` as whenever
abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() {
+ private val fakeLogBuffer =
+ LogBuffer(
+ name = "FakeLog",
+ maxSize = 1,
+ logcatEchoTracker =
+ object : LogcatEchoTracker {
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean =
+ true
+
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
+ },
+ systrace = false
+ )
+
private val leakCheck = LeakCheckedTest.SysuiLeakCheck()
protected val ambientDisplayConfiguration = FakeAmbientDisplayConfiguration(context)
protected val batteryController = FakeBatteryController(leakCheck)
protected val deviceProvisionedController = FakeDeviceProvisionedController()
protected val flags: NotifPipelineFlags = mock()
- protected val globalSettings = FakeGlobalSettings()
+ protected val globalSettings =
+ FakeGlobalSettings().also { it.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON) }
protected val headsUpManager: HeadsUpManager = mock()
protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
mock()
protected val keyguardStateController = FakeKeyguardStateController(leakCheck)
- protected val logger: NotificationInterruptLogger = mock()
protected val mainHandler = FakeHandler(Looper.getMainLooper())
+ protected val newLogger = VisualInterruptionDecisionLogger(fakeLogBuffer)
+ protected val oldLogger = NotificationInterruptLogger(fakeLogBuffer)
protected val powerManager: PowerManager = mock()
protected val statusBarStateController = FakeStatusBarStateController()
protected val systemClock = FakeSystemClock()
@@ -116,14 +137,9 @@
@Before
fun setUp() {
- globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON)
-
val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
userTracker.set(listOf(user), /* currentUserIndex = */ 0)
- whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
- .thenReturn(false)
-
provider.start()
}
@@ -203,24 +219,64 @@
}
@Test
- fun testShouldPeek_notQuiteOldEnoughWhen() {
+ fun testShouldPeek_oldWhen_now() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(0) })
+ }
+
+ @Test
+ fun testShouldPeek_oldWhen_notOldEnough() {
ensurePeekState()
assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS - 1) })
}
@Test
- fun testShouldPeek_zeroWhen() {
+ fun testShouldPeek_oldWhen_zeroWhen() {
ensurePeekState()
assertShouldHeadsUp(buildPeekEntry { whenMs = 0L })
}
@Test
- fun testShouldPeek_oldWhenButFsi() {
+ fun testShouldPeek_oldWhen_negativeWhen() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry { whenMs = -1L })
+ }
+
+ @Test
+ fun testShouldPeek_oldWhen_fullScreenIntent() {
ensurePeekState()
assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) })
}
@Test
+ fun testShouldPeek_oldWhen_foregroundService() {
+ ensurePeekState()
+ assertShouldHeadsUp(
+ buildPeekEntry {
+ whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS)
+ isForegroundService = true
+ }
+ )
+ }
+
+ @Test
+ fun testShouldPeek_oldWhen_userInitiatedJob() {
+ ensurePeekState()
+ assertShouldHeadsUp(
+ buildPeekEntry {
+ whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS)
+ isUserInitiatedJob = true
+ }
+ )
+ }
+
+ @Test
+ fun testShouldNotPeek_hiddenOnKeyguard() {
+ ensurePeekState({ keyguardShouldHideNotification = true })
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
fun testShouldPeek_defaultLegacySuppressor() {
ensurePeekState()
withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPeekEntry()) }
@@ -257,36 +313,6 @@
}
@Test
- fun testShouldPulse_defaultLegacySuppressor() {
- ensurePulseState()
- withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPulseEntry()) }
- }
-
- @Test
- fun testShouldNotPulse_legacySuppressInterruptions() {
- ensurePulseState()
- withLegacySuppressor(alwaysSuppressesInterruptions) {
- assertShouldNotHeadsUp(buildPulseEntry())
- }
- }
-
- @Test
- fun testShouldPulse_legacySuppressAwakeInterruptions() {
- ensurePulseState()
- withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
- assertShouldHeadsUp(buildPulseEntry())
- }
- }
-
- @Test
- fun testShouldPulse_legacySuppressAwakeHeadsUp() {
- ensurePulseState()
- withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
- assertShouldHeadsUp(buildPulseEntry())
- }
- }
-
- @Test
fun testShouldNotPulse_disabled() {
ensurePulseState { pulseOnNotificationsEnabled = false }
assertShouldNotHeadsUp(buildPulseEntry())
@@ -318,6 +344,42 @@
assertShouldNotHeadsUp(buildPulseEntry { importance = IMPORTANCE_LOW })
}
+ @Test
+ fun testShouldNotPulse_hiddenOnKeyguard() {
+ ensurePulseState({ keyguardShouldHideNotification = true })
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldPulse_defaultLegacySuppressor() {
+ ensurePulseState()
+ withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPulseEntry()) }
+ }
+
+ @Test
+ fun testShouldNotPulse_legacySuppressInterruptions() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+ }
+
+ @Test
+ fun testShouldPulse_legacySuppressAwakeInterruptions() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldHeadsUp(buildPulseEntry())
+ }
+ }
+
+ @Test
+ fun testShouldPulse_legacySuppressAwakeHeadsUp() {
+ ensurePulseState()
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldHeadsUp(buildPulseEntry())
+ }
+ }
+
private fun withPeekAndPulseEntry(
extendEntry: EntryBuilder.() -> Unit,
block: (NotificationEntry) -> Unit
@@ -330,73 +392,7 @@
}
@Test
- fun testShouldHeadsUp_groupedSummaryNotif_groupAlertAll() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_ALL
- }) {
- assertShouldHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldHeadsUp_groupedSummaryNotif_groupAlertSummary() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_SUMMARY
- }) {
- assertShouldHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldNotHeadsUp_groupedSummaryNotif_groupAlertChildren() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_CHILDREN
- }) {
- assertShouldNotHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldHeadsUp_ungroupedSummaryNotif_groupAlertChildren() {
- withPeekAndPulseEntry({
- isGrouped = false
- isGroupSummary = true
- groupAlertBehavior = GROUP_ALERT_CHILDREN
- }) {
- assertShouldHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldHeadsUp_groupedChildNotif_groupAlertAll() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = false
- groupAlertBehavior = GROUP_ALERT_ALL
- }) {
- assertShouldHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldHeadsUp_groupedChildNotif_groupAlertChildren() {
- withPeekAndPulseEntry({
- isGrouped = true
- isGroupSummary = false
- groupAlertBehavior = GROUP_ALERT_CHILDREN
- }) {
- assertShouldHeadsUp(it)
- }
- }
-
- @Test
- fun testShouldNotHeadsUp_groupedChildNotif_groupAlertSummary() {
+ fun testShouldNotHeadsUp_suppressiveGroupAlertBehavior() {
withPeekAndPulseEntry({
isGrouped = true
isGroupSummary = false
@@ -407,7 +403,18 @@
}
@Test
- fun testShouldHeadsUp_ungroupedChildNotif_groupAlertSummary() {
+ fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notSuppressive() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_suppressiveGroupAlertBehavior_notGrouped() {
withPeekAndPulseEntry({
isGrouped = false
isGroupSummary = false
@@ -435,18 +442,41 @@
}
@Test
- fun testShouldNotBubble_notAllowed() {
+ fun testShouldBubble_suppressiveGroupAlertBehavior() {
ensureBubbleState()
- assertShouldNotBubble(buildBubbleEntry { canBubble = false })
+ assertShouldBubble(
+ buildBubbleEntry {
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
+ }
+ )
}
@Test
- fun testShouldNotBubble_noBubbleMetadata() {
+ fun testShouldNotBubble_notABubble() {
+ ensureBubbleState()
+ assertShouldNotBubble(
+ buildBubbleEntry {
+ isBubble = false
+ hasBubbleMetadata = false
+ }
+ )
+ }
+
+ @Test
+ fun testShouldNotBubble_missingBubbleMetadata() {
ensureBubbleState()
assertShouldNotBubble(buildBubbleEntry { hasBubbleMetadata = false })
}
@Test
+ fun testShouldNotBubble_notAllowedToBubble() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { canBubble = false })
+ }
+
+ @Test
fun testShouldBubble_defaultLegacySuppressor() {
ensureBubbleState()
withLegacySuppressor(neverSuppresses) { assertShouldBubble(buildBubbleEntry()) }
@@ -477,13 +507,7 @@
}
@Test
- fun testShouldNotAlert_hiddenOnKeyguard() {
- ensurePeekState({ keyguardShouldHideNotification = true })
- assertShouldNotHeadsUp(buildPeekEntry())
-
- ensurePulseState({ keyguardShouldHideNotification = true })
- assertShouldNotHeadsUp(buildPulseEntry())
-
+ fun testShouldNotBubble_hiddenOnKeyguard() {
ensureBubbleState({ keyguardShouldHideNotification = true })
assertShouldNotBubble(buildBubbleEntry())
}
@@ -855,12 +879,12 @@
}
protected fun assertShouldHeadsUp(entry: NotificationEntry) =
- provider.makeUnloggedHeadsUpDecision(entry).let {
+ provider.makeAndLogHeadsUpDecision(entry).let {
assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt)
}
protected fun assertShouldNotHeadsUp(entry: NotificationEntry) =
- provider.makeUnloggedHeadsUpDecision(entry).let {
+ provider.makeAndLogHeadsUpDecision(entry).let {
assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt)
}
@@ -876,6 +900,7 @@
protected fun assertShouldFsi(entry: NotificationEntry) =
provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ provider.logFullScreenIntentDecision(it)
assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt)
}
@@ -884,10 +909,11 @@
expectWouldInterruptWithoutDnd: Boolean? = null
) =
provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ provider.logFullScreenIntentDecision(it)
assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt)
if (expectWouldInterruptWithoutDnd != null) {
assertEquals(
- "unexpected unsuppressed-without-DND FSI: ${it.logReason}",
+ "unexpected wouldInterruptWithoutDnd for FSI: ${it.logReason}",
expectWouldInterruptWithoutDnd,
it.wouldInterruptWithoutDnd
)
@@ -895,22 +921,35 @@
}
protected class EntryBuilder(val context: Context) {
- var importance = IMPORTANCE_DEFAULT
- var suppressedVisualEffects: Int? = null
- var whenMs: Long? = null
- var visibilityOverride: Int? = null
- var hasFsi = false
- var canBubble: Boolean? = null
- var isBubble = false
- var hasBubbleMetadata = false
+ // Set on BubbleMetadata:
var bubbleIsShortcut = false
- var bubbleSuppressesNotification: Boolean? = null
+ var bubbleSuppressesNotification = false
+
+ // Set on Notification.Builder:
+ var whenMs: Long? = null
var isGrouped = false
- var isGroupSummary: Boolean? = null
+ var isGroupSummary = false
var groupAlertBehavior: Int? = null
- var hasJustLaunchedFsi = false
+ var hasBubbleMetadata = false
+ var hasFsi = false
+
+ // Set on Notification:
+ var isForegroundService = false
+ var isUserInitiatedJob = false
+ var isBubble = false
var isStickyAndNotDemoted = false
- var packageSuspended: Boolean? = null
+
+ // Set on NotificationEntryBuilder:
+ var importance = IMPORTANCE_DEFAULT
+ var canBubble: Boolean? = null
+
+ // Set on NotificationEntry:
+ var hasJustLaunchedFsi = false
+
+ // Set on ModifiedRankingBuilder:
+ var packageSuspended = false
+ var visibilityOverride: Int? = null
+ var suppressedVisualEffects: Int? = null
private fun buildBubbleMetadata(): BubbleMetadata {
val builder =
@@ -928,71 +967,87 @@
)
}
- bubbleSuppressesNotification?.let { builder.setSuppressNotification(it) }
+ if (bubbleSuppressesNotification) {
+ builder.setSuppressNotification(true)
+ }
return builder.build()
}
fun build() =
Notification.Builder(context, TEST_CHANNEL_ID)
- .apply {
- setContentTitle(TEST_CONTENT_TITLE)
- setContentText(TEST_CONTENT_TEXT)
+ .also { nb ->
+ nb.setContentTitle(TEST_CONTENT_TITLE)
+ nb.setContentText(TEST_CONTENT_TEXT)
- if (hasFsi) {
- setFullScreenIntent(mock(), /* highPriority = */ true)
- }
-
- whenMs?.let { setWhen(it) }
-
- if (hasBubbleMetadata) {
- setBubbleMetadata(buildBubbleMetadata())
- }
+ whenMs?.let { nb.setWhen(it) }
if (isGrouped) {
- setGroup(TEST_GROUP_KEY)
+ nb.setGroup(TEST_GROUP_KEY)
}
- isGroupSummary?.let { setGroupSummary(it) }
+ if (isGroupSummary) {
+ nb.setGroupSummary(true)
+ }
- groupAlertBehavior?.let { setGroupAlertBehavior(it) }
+ groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) }
+
+ if (hasBubbleMetadata) {
+ nb.setBubbleMetadata(buildBubbleMetadata())
+ }
+
+ if (hasFsi) {
+ nb.setFullScreenIntent(mock(), /* highPriority = */ true)
+ }
}
.build()
- .apply {
+ .also { n ->
+ if (isForegroundService) {
+ n.flags = n.flags or FLAG_FOREGROUND_SERVICE
+ }
+
+ if (isUserInitiatedJob) {
+ n.flags = n.flags or FLAG_USER_INITIATED_JOB
+ }
+
if (isBubble) {
- flags = flags or FLAG_BUBBLE
+ n.flags = n.flags or FLAG_BUBBLE
}
if (isStickyAndNotDemoted) {
- flags = flags or FLAG_FSI_REQUESTED_BUT_DENIED
+ n.flags = n.flags or FLAG_FSI_REQUESTED_BUT_DENIED
}
}
.let { NotificationEntryBuilder().setNotification(it) }
- .apply {
- setPkg(TEST_PACKAGE)
- setOpPkg(TEST_PACKAGE)
- setTag(TEST_TAG)
+ .also { neb ->
+ neb.setPkg(TEST_PACKAGE)
+ neb.setOpPkg(TEST_PACKAGE)
+ neb.setTag(TEST_TAG)
- setImportance(importance)
- setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
+ neb.setImportance(importance)
+ neb.setChannel(
+ NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
+ )
- canBubble?.let { setCanBubble(it) }
+ canBubble?.let { neb.setCanBubble(it) }
}
.build()!!
- .also {
+ .also { ne ->
if (hasJustLaunchedFsi) {
- it.notifyFullScreenIntentLaunched()
+ ne.notifyFullScreenIntentLaunched()
}
if (isStickyAndNotDemoted) {
- assertFalse(it.isDemoted)
+ assertFalse(ne.isDemoted)
}
- modifyRanking(it)
- .apply {
- suppressedVisualEffects?.let { setSuppressedVisualEffects(it) }
- visibilityOverride?.let { setVisibilityOverride(it) }
- packageSuspended?.let { setSuspended(it) }
+ modifyRanking(ne)
+ .also { mrb ->
+ if (packageSuspended) {
+ mrb.setSuspended(true)
+ }
+ visibilityOverride?.let { mrb.setVisibilityOverride(it) }
+ suppressedVisualEffects?.let { mrb.setSuppressedVisualEffects(it) }
}
.build()
}
@@ -1013,6 +1068,7 @@
}
protected fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ isBubble = true
canBubble = true
hasBubbleMetadata = true
run(block)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d1b9b8a..0b87fe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -703,6 +704,7 @@
mKeyguardStateController,
mShadeViewController,
mStatusBarStateController,
+ mock(StatusBarIconViewBindingFailureTracker.class),
mCommandQueue,
mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 7456e00..8c823b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -20,7 +20,6 @@
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
@@ -68,7 +67,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialogController;
@@ -149,14 +147,13 @@
}
};
- private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
private FakeSettings mSecureSettings;
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
- @Before
+ @Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -179,8 +176,6 @@
mConfigurationController = new FakeConfigurationController();
- mFeatureFlags = new FakeFeatureFlags();
-
mSecureSettings = new FakeSettings();
when(mLazySecureSettings.get()).thenReturn(mSecureSettings);
@@ -200,7 +195,6 @@
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags,
mLazySecureSettings);
mDialog.init(0, null);
State state = createShellState();
@@ -328,7 +322,6 @@
@Test
public void testVibrateOnRingerChangedToVibrate() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialSilentState = new State();
initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
@@ -349,30 +342,7 @@
}
@Test
- public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialSilentState = new State();
- initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // change ringer to silent
- mDialog.onStateChangedH(initialSilentState);
-
- // expected: shouldn't call vibrate yet
- verify(mVolumeDialogController, never()).vibrate(any());
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // expected: vibrate method of controller is not used
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testNoVibrateOnRingerInitialization() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = -1;
@@ -390,29 +360,9 @@
}
@Test
- public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = -1;
-
- // ringer not initialized yet:
- mDialog.onStateChangedH(initialUnsetState);
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // shouldn't call vibrate on the controller either
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testSelectVibrateFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -426,27 +376,9 @@
}
@Test
- public void testSelectVibrateFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerVibrate.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_VIBRATE, false);
- }
-
- @Test
public void testSelectMuteFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -460,27 +392,9 @@
}
@Test
- public void testSelectMuteFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerMute.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_SILENT, false);
- }
-
- @Test
public void testSelectNormalFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
mDialog.onStateChangedH(initialUnsetState);
@@ -493,23 +407,6 @@
AudioManager.RINGER_MODE_NORMAL, false);
}
- @Test
- public void testSelectNormalFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerNormal.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
- }
-
/**
* Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
* API does not exist. So we do the next best thing; we check the cached icon id.
@@ -682,7 +579,6 @@
State state = createShellState();
state.ringerModeInternal = ringerMode;
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
mDialog.onStateChangedH(state);
mDialog.show(SHOW_REASON_UNKNOWN);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 6e9363b..af1930e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -60,6 +60,10 @@
override val minPatternLength: Int = 4
+ private val _isPinEnhancedPrivacyEnabled = MutableStateFlow(false)
+ override val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
+ _isPinEnhancedPrivacyEnabled.asStateFlow()
+
private var failedAttemptCount = 0
private var throttlingEndTimestamp = 0L
private var credentialOverride: List<Any>? = null
@@ -138,6 +142,10 @@
}
}
+ fun setPinEnhancedPrivacyEnabled(isEnabled: Boolean) {
+ _isPinEnhancedPrivacyEnabled.value = isEnabled
+ }
+
private fun getExpectedCredential(securityMode: SecurityMode): List<Any> {
return when (val credentialType = getCurrentCredentialType(securityMode)) {
LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
index 0c5e438..005cac4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
@@ -19,10 +19,15 @@
import android.hardware.biometrics.SensorLocationInternal
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
+@SysUISingleton
+class FakeFingerprintPropertyRepository @Inject constructor() : FingerprintPropertyRepository {
private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
override val sensorId = _sensorId.asStateFlow()
@@ -50,4 +55,29 @@
_sensorType.value = sensorType
_sensorLocations.value = sensorLocations
}
+
+ /** setProperties as if the device supports UDFPS_OPTICAL. */
+ fun supportsUdfps() {
+ setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+ sensorLocations = emptyMap(),
+ )
+ }
+
+ /** setProperties as if the device supports the rear fingerprint sensor. */
+ fun supportsRearFps() {
+ setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = FingerprintSensorType.REAR,
+ sensorLocations = emptyMap(),
+ )
+ }
+}
+
+@Module
+interface FakeFingerprintPropertyRepositoryModule {
+ @Binds fun bindFake(fake: FakeFingerprintPropertyRepository): FingerprintPropertyRepository
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
index 44286b7..8ff04a63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -15,17 +15,23 @@
package com.android.systemui.deviceentry.data
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeTrustRepositoryModule
import dagger.Module
@Module(
includes =
[
+ FakeBiometricSettingsRepositoryModule::class,
FakeDeviceEntryRepositoryModule::class,
- FakeTrustRepositoryModule::class,
FakeDeviceEntryFaceAuthRepositoryModule::class,
+ FakeDeviceEntryFingerprintAuthRepositoryModule::class,
+ FakeFingerprintPropertyRepositoryModule::class,
+ FakeTrustRepositoryModule::class,
]
)
object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index 85261123..df31a12 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -18,13 +18,18 @@
package com.android.systemui.keyguard.data.repository
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-class FakeBiometricSettingsRepository : BiometricSettingsRepository {
+@SysUISingleton
+class FakeBiometricSettingsRepository @Inject constructor() : BiometricSettingsRepository {
private val _isFingerprintEnrolledAndEnabled = MutableStateFlow(false)
override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean>
get() = _isFingerprintEnrolledAndEnabled
@@ -97,3 +102,8 @@
}
}
}
+
+@Module
+interface FakeBiometricSettingsRepositoryModule {
+ @Binds fun bindFake(fake: FakeBiometricSettingsRepository): BiometricSettingsRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 38791ca..c9160ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -17,14 +17,20 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
-class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
+@SysUISingleton
+class FakeDeviceEntryFingerprintAuthRepository @Inject constructor() :
+ DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
fun setLockedOut(lockedOut: Boolean) {
@@ -52,3 +58,11 @@
_authenticationStatus.value = status
}
}
+
+@Module
+interface FakeDeviceEntryFingerprintAuthRepositoryModule {
+ @Binds
+ fun bindFake(
+ fake: FakeDeviceEntryFingerprintAuthRepository
+ ): DeviceEntryFingerprintAuthRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index b90ad8c..3674244 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -151,6 +151,17 @@
_transitions.emit(step)
}
+ suspend fun sendTransitionSteps(
+ steps: List<TransitionStep>,
+ testScope: TestScope,
+ validateStep: Boolean = true
+ ) {
+ steps.forEach {
+ sendTransitionStep(it, validateStep = validateStep)
+ testScope.testScheduler.runCurrent()
+ }
+ }
+
override fun startTransition(info: TransitionInfo): UUID? {
return if (info.animator == null) UUID.randomUUID() else null
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
index 1efa74b..62765d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
@@ -20,7 +20,6 @@
class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
- var handleResult: Boolean = false
var policyResult: DisabledByPolicyInteractor.PolicyResult =
DisabledByPolicyInteractor.PolicyResult.TileEnabled
@@ -31,5 +30,9 @@
override fun handlePolicyResult(
policyResult: DisabledByPolicyInteractor.PolicyResult
- ): Boolean = handleResult
+ ): Boolean =
+ when (policyResult) {
+ is DisabledByPolicyInteractor.PolicyResult.TileEnabled -> false
+ is DisabledByPolicyInteractor.PolicyResult.TileDisabled -> true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
index 2b3330f..3fcf8a9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -17,16 +17,21 @@
package com.android.systemui.qs.tiles.base.interactor
import android.os.UserHandle
-import javax.annotation.CheckReturnValue
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
-class FakeQSTileDataInteractor<T>(
- private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = Int.MAX_VALUE),
- private val availabilityFlow: MutableSharedFlow<Boolean> =
- MutableSharedFlow(replay = Int.MAX_VALUE),
-) : QSTileDataInteractor<T> {
+class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
+
+ private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = 1)
+ val dataSubscriptionCount
+ get() = dataFlow.subscriptionCount
+ private val availabilityFlow: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
+ val availabilitySubscriptionCount
+ get() = availabilityFlow.subscriptionCount
+
+ private val mutableTriggers = mutableListOf<DataUpdateTrigger>()
+ val triggers: List<DataUpdateTrigger> = mutableTriggers
private val mutableDataRequests = mutableListOf<DataRequest>()
val dataRequests: List<DataRequest> = mutableDataRequests
@@ -34,14 +39,17 @@
private val mutableAvailabilityRequests = mutableListOf<AvailabilityRequest>()
val availabilityRequests: List<AvailabilityRequest> = mutableAvailabilityRequests
- @CheckReturnValue fun emitData(data: T): Boolean = dataFlow.tryEmit(data)
+ suspend fun emitData(data: T): Unit = dataFlow.emit(data)
fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
mutableDataRequests.add(DataRequest(user))
- return triggers.flatMapLatest { dataFlow }
+ return triggers.flatMapLatest {
+ mutableTriggers.add(it)
+ dataFlow
+ }
}
override fun availability(user: UserHandle): Flow<Boolean> {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
index de72a7d..d231d63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
@@ -24,6 +24,8 @@
override fun getConfig(tileSpec: String): QSTileConfig = configs.getValue(tileSpec)
+ override fun hasConfig(tileSpec: String): Boolean = configs.containsKey(tileSpec)
+
fun putConfig(tileSpec: TileSpec, config: QSTileConfig) {
configs[tileSpec.spec] = config
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index ec12d21..fc4ed1d 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -28,9 +28,8 @@
name: "ravenwood-junit",
srcs: ["junit-src/**/*.java"],
libs: [
+ "framework-minus-apex.ravenwood",
"junit",
],
- sdk_version: "core_current",
- host_supported: true,
visibility: ["//visibility:public"],
}
diff --git a/ravenwood/README-ravenwood+mockito.md b/ravenwood/README-ravenwood+mockito.md
new file mode 100644
index 0000000..6adb6144
--- /dev/null
+++ b/ravenwood/README-ravenwood+mockito.md
@@ -0,0 +1,24 @@
+# Ravenwood and Mockito
+
+Last update: 2023-11-13
+
+- As of 2023-11-13, `external/mockito` is based on version 2.x.
+- Mockito didn't support static mocking before 3.4.0.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+- Latest Mockito is 5.*. According to https://github.com/mockito/mockito:
+ `Mockito 3 does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. Mockito 4 removes deprecated API. Mockito 5 switches the default mockmaker to mockito-inline, and now requires Java 11.`
+
+- Mockito now supports Android natively.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1
+ - But it's unclear at this point to omakoto@ how the `mockito-android` module is built.
+
+- Potential plan:
+ - Ideal option:
+ - If we can update `external/mockito`, that'd be great, but it may not work because
+ Mockito has removed the deprecated APIs.
+ - Second option:
+ - Import the latest mockito as `external/mockito-new`, and require ravenwood
+ to use this one.
+ - The latest mockito needs be exposed to all of 1) device tests, 2) host tests, and 3) ravenwood tests.
+ - This probably will require the latest `bytebuddy` and `objenesis`.
\ No newline at end of file
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
index 76964a7..7dc197e 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
index ddf65dc..1d31579 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
similarity index 93%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
index d7ef7f5..d2c77c1 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
@@ -35,5 +35,5 @@
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodWholeClassKeep {
+public @interface RavenwoodKeepWholeClass {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
index 8cdc1ff..4b9cf85 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
index 759c918..6727327 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
similarity index 83%
copy from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
copy to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
index 8cdc1ff..a920f63 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
-import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -29,8 +29,7 @@
*
* @hide
*/
-@Target({TYPE})
+@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
- String value();
+public @interface RavenwoodReplace {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
index de3dd04..a234a9b 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
deleted file mode 100644
index 5a0a8f4..0000000
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
+++ /dev/null
@@ -1,38 +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.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.ravenwood.annotations;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * TODO: Javadoc
- *
- * @hide
- */
-@Target({METHOD})
-@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodSubstitute {
- // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
- // value with ASM doesn't seem trivial. (? not sure.)
- String suffix();
-}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 48c0a2d..692d598 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -76,21 +76,13 @@
class android.util.UtilConfig stubclass
# Internals
-class com.android.internal.util.ArrayUtils stubclass
- method newUnpaddedByteArray (I)[B @newUnpaddedByteArray$ravenwood
- method newUnpaddedCharArray (I)[C @newUnpaddedCharArray$ravenwood
- method newUnpaddedIntArray (I)[I @newUnpaddedIntArray$ravenwood
- method newUnpaddedBooleanArray (I)[Z @newUnpaddedBooleanArray$ravenwood
- method newUnpaddedLongArray (I)[J @newUnpaddedLongArray$ravenwood
- method newUnpaddedFloatArray (I)[F @newUnpaddedFloatArray$ravenwood
- method newUnpaddedObjectArray (I)[Ljava/lang/Object; @newUnpaddedObjectArray$ravenwood
- method newUnpaddedArray (Ljava/lang/Class;I)[Ljava/lang/Object; @newUnpaddedArray$ravenwood
-
class com.android.internal.util.GrowingArrayUtils stubclass
class com.android.internal.util.LineBreakBufferedWriter stubclass
class com.android.internal.util.Preconditions stubclass
class com.android.internal.util.StringPool stubclass
+class com.android.internal.os.SomeArgs stubclass
+
# Parcel
class android.os.Parcel stubclass
method writeException (Ljava/lang/Exception;)V @writeException$ravenwood
@@ -102,14 +94,16 @@
class android.os.BadParcelableException stubclass
class android.os.BadTypeParcelableException stubclass
-# Binder: just enough to construct, no further functionality
-class android.os.Binder stub
- method <init> ()V stub
- method <init> (Ljava/lang/String;)V stub
- method isDirectlyHandlingTransaction ()Z stub
- method isDirectlyHandlingTransactionNative ()Z @isDirectlyHandlingTransactionNative$ravenwood
- method getNativeBBinderHolder ()J @getNativeBBinderHolder$ravenwood
+# Binder
+class android.os.DeadObjectException stubclass
+class android.os.DeadSystemException stubclass
+class android.os.RemoteException stubclass
+class android.os.TransactionTooLargeException stubclass
# Containers
class android.os.BaseBundle stubclass
class android.os.Bundle stubclass
+
+# Misc
+class android.os.PatternMatcher stubclass
+class android.os.ParcelUuid stubclass
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a6b3f66..bffd0cd 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -16,6 +16,7 @@
package android.platform.test.ravenwood;
+import android.os.Process;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import org.junit.Assume;
@@ -23,6 +24,8 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
* QUESTIONS ABOUT IT.
@@ -30,20 +33,84 @@
* @hide
*/
public class RavenwoodRule implements TestRule {
+ private static AtomicInteger sNextPid = new AtomicInteger(100);
+
+ /**
+ * Unless the test author requests differently, run as "nobody", and give each collection of
+ * tests its own unique PID.
+ */
+ private int mUid = android.os.Process.NOBODY_UID;
+ private int mPid = sNextPid.getAndIncrement();
+
+ public RavenwoodRule() {
+ }
+
+ public static class Builder {
+ private RavenwoodRule mRule = new RavenwoodRule();
+
+ public Builder() {
+ }
+
+ /**
+ * Configure the identity of this process to be the system UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessSystem() {
+ mRule.mUid = android.os.Process.SYSTEM_UID;
+ return this;
+ }
+
+ /**
+ * Configure the identity of this process to be an app UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessApp() {
+ mRule.mUid = android.os.Process.FIRST_APPLICATION_UID;
+ return this;
+ }
+
+ public RavenwoodRule build() {
+ return mRule;
+ }
+ }
+
+ /**
+ * Return if the current process is running under a Ravenwood test environment.
+ */
public boolean isUnderRavenwood() {
// TODO: give ourselves a better environment signal
return System.getProperty("java.class.path").contains("ravenwood");
}
+ private void init() {
+ android.os.Process.init$ravenwood(mUid, mPid);
+ android.os.Binder.init$ravenwood();
+ }
+
+ private void reset() {
+ android.os.Process.reset$ravenwood();
+ android.os.Binder.reset$ravenwood();
+ }
+
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ final boolean isUnderRavenwood = isUnderRavenwood();
if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
- Assume.assumeFalse(isUnderRavenwood());
+ Assume.assumeFalse(isUnderRavenwood);
}
- base.evaluate();
+ if (isUnderRavenwood) {
+ init();
+ }
+ try {
+ base.evaluate();
+ } finally {
+ if (isUnderRavenwood) {
+ reset();
+ }
+ }
}
};
}
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/mockito/Android.bp
new file mode 100644
index 0000000..4135022
--- /dev/null
+++ b/ravenwood/mockito/Android.bp
@@ -0,0 +1,72 @@
+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"],
+}
+
+// Ravenwood tests run on the hostside, so we need mockit of the host variant.
+// But we need to use it in modules of the android variant, so we "wash" the variant with it.
+java_host_for_device {
+ name: "mockito_ravenwood",
+ libs: [
+ "mockito",
+ "objenesis",
+ ],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodMockitoTest",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "mockito_ravenwood",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ auto_gen_config: true,
+}
+
+android_test {
+ name: "RavenwoodMockitoTest_device",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "androidx.test.rules",
+
+ "ravenwood-junit",
+
+ "mockito-target-extended-minus-junit4",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ // Required by mockito
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/mockito/AndroidManifest.xml
new file mode 100644
index 0000000..15f0a29
--- /dev/null
+++ b/ravenwood/mockito/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.ravenwood.mockitotest">
+
+ <application android:debuggable="true" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.ravenwood.mockitotest"
+ />
+</manifest>
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/mockito/AndroidTest.xml
new file mode 100644
index 0000000..96bc275
--- /dev/null
+++ b/ravenwood/mockito/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="RavenwoodMockitoTest_device.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.ravenwood.mockitotest" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
new file mode 100644
index 0000000..36fa3dd
--- /dev/null
+++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.ravenwood.mockito;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public class RavenwoodMockitoTest {
+ @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+
+// Use this to mock static methods, which isn't supported by mockito 2.
+// Mockito supports static mocking since 3.4.0:
+// See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+// private MockitoSession mMockingSession;
+//
+// @Before
+// public void setUp() {
+// mMockingSession = mockitoSession()
+// .strictness(Strictness.LENIENT)
+// .mockStatic(RavenwoodMockitoTest.class)
+// .startMocking();
+// }
+//
+// @After
+// public void tearDown() {
+// if (mMockingSession != null) {
+// mMockingSession.finishMocking();
+// }
+// }
+
+ @Test
+ public void testMockJdkClass() {
+ Process object = mock(Process.class);
+
+ when(object.exitValue()).thenReturn(42);
+
+ assertThat(object.exitValue()).isEqualTo(42);
+ }
+
+ /*
+ - Intent can't be mocked because of the dependency to `org.xmlpull.v1.XmlPullParser`.
+ (The error says "Mockito can only mock non-private & non-final classes", but that's likely a
+ red-herring.)
+
+STACKTRACE:
+org.mockito.exceptions.base.MockitoException:
+Mockito cannot mock this class: class android.content.Intent.
+
+ :
+
+Underlying exception : java.lang.IllegalArgumentException: Could not create type
+ at com.android.ravenwood.mockito.RavenwoodMockitoTest.testMockAndroidClass1
+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+
+ :
+
+Caused by: java.lang.ClassNotFoundException: org.xmlpull.v1.XmlPullParser
+ at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
+ at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
+ at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
+ ... 54 more
+ */
+ @Test
+ @IgnoreUnderRavenwood
+ public void testMockAndroidClass1() {
+ Intent object = mock(Intent.class);
+
+ when(object.getAction()).thenReturn("ACTION_RAVENWOOD");
+
+ assertThat(object.getAction()).isEqualTo("ACTION_RAVENWOOD");
+ }
+
+ @Test
+ public void testMockAndroidClass2() {
+ Context object = mock(Context.class);
+
+ when(object.getPackageName()).thenReturn("android");
+
+ assertThat(object.getPackageName()).isEqualTo("android");
+ }
+}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 0811f90..776a19a 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -1,2 +1,9 @@
# Only classes listed here can use the Ravenwood annotations.
+com.android.internal.util.ArrayUtils
+
+android.os.Binder
+android.os.Binder$IdentitySupplier
+android.os.IBinder
+android.os.Process
+android.os.SystemClock
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index 6e1384f..4b07ef6 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -16,22 +16,22 @@
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
--keep-annotation
- android.ravenwood.annotations.RavenwoodKeep
+ android.ravenwood.annotation.RavenwoodKeep
--keep-class-annotation
- android.ravenwood.annotations.RavenwoodWholeClassKeep
+ android.ravenwood.annotation.RavenwoodKeepWholeClass
--throw-annotation
- android.ravenwood.annotations.RavenwoodThrow
+ android.ravenwood.annotation.RavenwoodThrow
--remove-annotation
- android.ravenwood.annotations.RavenwoodRemove
+ android.ravenwood.annotation.RavenwoodRemove
--substitute-annotation
- android.ravenwood.annotations.RavenwoodSubstitute
+ android.ravenwood.annotation.RavenwoodReplace
--native-substitute-annotation
- android.ravenwood.annotations.RavenwoodNativeSubstitutionClass
+ android.ravenwood.annotation.RavenwoodNativeSubstitutionClass
--class-load-hook-annotation
- android.ravenwood.annotations.RavenwoodClassLoadHook
+ android.ravenwood.annotation.RavenwoodClassLoadHook
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 2c60893..b9c269c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -522,7 +522,8 @@
private void notifyListeners(
@UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
mListeners.broadcast((listener, callbackUserId) -> {
- if ((int) callbackUserId == userId) {
+ int listenerUserId = (int) callbackUserId;
+ if (listenerUserId == userId || listenerUserId == UserHandle.USER_ALL) {
try {
listener.onAssociationsChanged(associations);
} catch (RemoteException ignored) {
@@ -660,6 +661,9 @@
enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId);
+ if (userId == UserHandle.USER_ALL) {
+ return List.copyOf(mAssociationStore.getAssociations());
+ }
return mAssociationStore.getAssociationsForUser(userId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index ae8fddf..562fe3b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -304,7 +304,7 @@
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
- mPersistentDeviceId = PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationInfo.getId();
+ mPersistentDeviceId = createPersistentDeviceId(associationInfo.getId());
mService = service;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -380,6 +380,10 @@
return mSensorController;
}
+ static String createPersistentDeviceId(int associationId) {
+ return PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationId;
+ }
+
/**
* Returns the flags that should be added to any virtual displays created on this virtual
* device.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 92af68b..215970e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -27,6 +27,7 @@
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -94,6 +95,11 @@
private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native";
+ private static final List<String> VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES = Arrays.asList(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerNativeImpl mNativeImpl;
@@ -105,6 +111,9 @@
private static AtomicInteger sNextUniqueIndex = new AtomicInteger(
Context.DEVICE_ID_DEFAULT + 1);
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private List<AssociationInfo> mActiveAssociations = new ArrayList<>();
+
private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
new CompanionDeviceManager.OnAssociationsChangedListener() {
@Override
@@ -161,6 +170,7 @@
};
@Override
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
if (Flags.enableNativeVdm()) {
@@ -172,6 +182,21 @@
activityTaskManagerInternal.registerActivityStartInterceptor(
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
mActivityInterceptorCallback);
+
+ if (Flags.persistentDeviceIdApi()) {
+ CompanionDeviceManager cdm =
+ getContext().getSystemService(CompanionDeviceManager.class);
+ if (cdm != null) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mActiveAssociations = cdm.getAllAssociations(UserHandle.USER_ALL);
+ }
+ cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
+ this::onCdmAssociationsChanged, UserHandle.USER_ALL);
+ } else {
+ Slog.e(TAG, "Failed to find CompanionDeviceManager. No CDM association info "
+ + " will be available.");
+ }
+ }
}
void onCameraAccessBlocked(int appUid) {
@@ -264,9 +289,11 @@
try {
getContext().sendBroadcastAsUser(i, UserHandle.ALL);
- synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
- unregisterCdmAssociationListener();
+ if (!Flags.persistentDeviceIdApi()) {
+ synchronized (mVirtualDeviceManagerLock) {
+ if (mVirtualDevices.size() == 0) {
+ unregisterCdmAssociationListener();
+ }
}
}
} finally {
@@ -316,6 +343,45 @@
cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ void onCdmAssociationsChanged(List<AssociationInfo> associations) {
+ Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
+ Set<String> removedPersistentDeviceIds = new HashSet<>();
+ synchronized (mVirtualDeviceManagerLock) {
+ Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
+ for (int i = 0; i < associations.size(); ++i) {
+ activeAssociationIds.add(associations.get(i).getId());
+ }
+
+ for (int i = 0; i < mActiveAssociations.size(); ++i) {
+ AssociationInfo associationInfo = mActiveAssociations.get(i);
+ if (!activeAssociationIds.contains(associationInfo.getId())
+ && VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
+ associationInfo.getDeviceProfile())) {
+ removedPersistentDeviceIds.add(
+ VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
+ }
+ }
+
+ for (int i = 0; i < mVirtualDevices.size(); i++) {
+ VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
+ if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) {
+ virtualDevicesToRemove.add(virtualDevice);
+ }
+ }
+
+ mActiveAssociations = associations;
+ }
+
+ for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
+ virtualDevice.close();
+ }
+
+ if (!removedPersistentDeviceIds.isEmpty()) {
+ mLocalService.onPersistentDeviceIdsRemoved(removedPersistentDeviceIds);
+ }
+ }
+
private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() {
synchronized (mVirtualDeviceManagerLock) {
ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size());
@@ -393,7 +459,7 @@
}
synchronized (mVirtualDeviceManagerLock) {
- if (mVirtualDevices.size() == 0) {
+ if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
final long callindId = Binder.clearCallingIdentity();
try {
registerCdmAssociationListener();
@@ -441,10 +507,8 @@
+ " is not the owner of the supplied VirtualDevice");
}
- int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback,
- packageName);
- mLocalService.onVirtualDisplayCreated(displayId);
- return displayId;
+ return virtualDeviceImpl.createVirtualDisplay(
+ virtualDisplayConfig, callback, packageName);
}
@Override // Binder call
@@ -625,11 +689,12 @@
private final class LocalService extends VirtualDeviceManagerInternal {
@GuardedBy("mVirtualDeviceManagerLock")
- private final ArrayList<VirtualDisplayListener>
- mVirtualDisplayListeners = new ArrayList<>();
+ private final ArrayList<AppsOnVirtualDeviceListener> mAppsOnVirtualDeviceListeners =
+ new ArrayList<>();
@GuardedBy("mVirtualDeviceManagerLock")
- private final ArrayList<AppsOnVirtualDeviceListener>
- mAppsOnVirtualDeviceListeners = new ArrayList<>();
+ private final ArrayList<Consumer<String>> mPersistentDeviceIdRemovedListeners =
+ new ArrayList<>();
+
@GuardedBy("mVirtualDeviceManagerLock")
private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
@@ -665,35 +730,15 @@
}
@Override
- public void onVirtualDisplayCreated(int displayId) {
- final VirtualDisplayListener[] listeners;
- synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
- }
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayCreated(displayId);
- }
- });
- }
-
- @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
- final VirtualDisplayListener[] listeners;
VirtualDeviceImpl virtualDeviceImpl;
synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
virtualDeviceImpl = mVirtualDevices.get(
((VirtualDeviceImpl) virtualDevice).getDeviceId());
}
if (virtualDeviceImpl != null) {
virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
}
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayRemoved(displayId);
- }
- });
}
@Override
@@ -725,6 +770,22 @@
}
@Override
+ public void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds) {
+ final List<Consumer<String>> persistentDeviceIdRemovedListeners;
+ synchronized (mVirtualDeviceManagerLock) {
+ persistentDeviceIdRemovedListeners = List.copyOf(
+ mPersistentDeviceIdRemovedListeners);
+ }
+ mHandler.post(() -> {
+ for (String persistentDeviceId : removedPersistentDeviceIds) {
+ for (Consumer<String> listener : persistentDeviceIdRemovedListeners) {
+ listener.accept(persistentDeviceId);
+ }
+ }
+ });
+ }
+
+ @Override
public void onAuthenticationPrompt(int uid) {
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mVirtualDevices.size(); i++) {
@@ -791,6 +852,10 @@
@Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
+ if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ }
+
VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
virtualDevice = mVirtualDevices.get(deviceId);
@@ -799,22 +864,6 @@
}
@Override
- public void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
- synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.add(listener);
- }
- }
-
- @Override
- public void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
- synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.remove(listener);
- }
- }
-
- @Override
public void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
@@ -829,6 +878,22 @@
mAppsOnVirtualDeviceListeners.remove(listener);
}
}
+
+ @Override
+ public void registerPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mPersistentDeviceIdRemovedListeners.add(persistentDeviceIdRemovedListener);
+ }
+ }
+
+ @Override
+ public void unregisterPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mPersistentDeviceIdRemovedListeners.remove(persistentDeviceIdRemovedListener);
+ }
+ }
}
private static final class PendingTrampolineMap {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 065a447..4b00434 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -43,6 +43,7 @@
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -54,7 +55,6 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.snapshot.PackageDataSnapshot;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9bba08a..ddccce5 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2230,8 +2230,10 @@
|| now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
// recent memory, so we will keep its process ahead
- // of the background processes.
- if (adj > SERVICE_ADJ) {
+ // of the background processes. This does not apply
+ // to the SDK sandbox process since it should never
+ // be more important than its corresponding app.
+ if (!app.isSdkSandbox && adj > SERVICE_ADJ) {
adj = SERVICE_ADJ;
state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e5f7637..7292ea6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -148,6 +148,7 @@
import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.Clock;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -165,7 +166,6 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.policy.AppOpsPolicy;
import dalvik.annotation.optimization.NeverCompile;
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 283353dd..c629b2b 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -25,6 +25,7 @@
import android.util.ArraySet;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Virtual device manager local service interface.
@@ -32,29 +33,12 @@
*/
public abstract class VirtualDeviceManagerInternal {
- /** Interface to listen to the creation and destruction of virtual displays. */
- public interface VirtualDisplayListener {
- /** Notifies that a virtual display was created. */
- void onVirtualDisplayCreated(int displayId);
-
- /** Notifies that a virtual display was removed. */
- void onVirtualDisplayRemoved(int displayId);
- }
-
/** Interface to listen to the changes on the list of app UIDs running on any virtual device. */
public interface AppsOnVirtualDeviceListener {
/** Notifies that running apps on any virtual device has changed */
void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids);
}
- /** Register a listener for the creation and destruction of virtual displays. */
- public abstract void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
- /** Unregister a listener for the creation and destruction of virtual displays. */
- public abstract void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
/** Register a listener for changes of running app UIDs on any virtual device. */
public abstract void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
@@ -63,6 +47,14 @@
public abstract void unregisterAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
+ /** Register a listener for removal of persistent device IDs. */
+ public abstract void registerPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener);
+
+ /** Unregister a listener for the removal of persistent device IDs. */
+ public abstract void unregisterPersistentDeviceIdRemovedListener(
+ @NonNull Consumer<String> persistentDeviceIdRemovedListener);
+
/**
* Notifies that the set of apps running on virtual devices has changed.
* This method only notifies the listeners when the union of running UIDs on all virtual devices
@@ -76,6 +68,11 @@
public abstract void onAuthenticationPrompt(int uid);
/**
+ * Notifies the given persistent device IDs have been removed.
+ */
+ public abstract void onPersistentDeviceIdsRemoved(Set<String> removedPersistentDeviceIds);
+
+ /**
* Gets the owner uid for a deviceId.
*
* @param deviceId which device we're asking about
@@ -104,13 +101,6 @@
public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid);
/**
- * Notifies that a virtual display is created.
- *
- * @param displayId The display id of the created virtual display.
- */
- public abstract void onVirtualDisplayCreated(int displayId);
-
- /**
* Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d023913..fb6c9e3 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -724,8 +724,9 @@
|| mode.getPhysicalHeight() > maxAllowedHeight
|| mode.getPhysicalWidth() < outSummary.minWidth
|| mode.getPhysicalHeight() < outSummary.minHeight
- || mode.getRefreshRate() < outSummary.minPhysicalRefreshRate
- || mode.getRefreshRate() > outSummary.maxPhysicalRefreshRate) {
+ || mode.getRefreshRate() < (outSummary.minPhysicalRefreshRate - FLOAT_TOLERANCE)
+ || mode.getRefreshRate() > (outSummary.maxPhysicalRefreshRate + FLOAT_TOLERANCE)
+ ) {
continue;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index d3eedd7..1687157 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -191,9 +191,16 @@
launchDeviceDiscovery();
startQueuedActions();
if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildRequestActiveSource(
- getDeviceInfo().getLogicalAddress()));
+ addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
+ getDeviceInfo().getLogicalAddress(),
+ getDeviceInfo().getPhysicalAddress()));
+ }
+ }
+ }));
}
}
@@ -1325,6 +1332,8 @@
removeAction(TimerRecordingAction.class);
removeAction(NewDeviceAction.class);
removeAction(AbsoluteVolumeAudioStatusAction.class);
+ // Remove pending actions.
+ removeAction(RequestActiveSourceAction.class);
// Keep SAM enabled if eARC is enabled, unless we're going to Standby.
if (initiatedByCec || !mService.isEarcEnabled()){
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
new file mode 100644
index 0000000..017c86d
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -0,0 +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.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.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
+
+/**
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ */
+public class RequestActiveSourceAction extends HdmiCecFeatureAction {
+ private static final String TAG = "RequestActiveSourceAction";
+
+ // State to wait for the <Active Source> message.
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1;
+
+ RequestActiveSourceAction(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
+ super(source, callback);
+ }
+
+ @Override
+ boolean start() {
+ Slog.v(TAG, "RequestActiveSourceAction started.");
+
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ // The action finishes successfully if the <Active Source> message is received.
+ // {@link HdmiCecLocalDevice#onMessage} handles this message, so false is returned.
+ if (cmd.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+ if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) {
+ finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 707e990..8c75367 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -360,6 +360,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -5245,13 +5246,23 @@
}
}
+ // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
@Override
public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
- enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+ enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
return mZenModeHelper.getZenRules();
}
@Override
+ public Map<String, AutomaticZenRule> getAutomaticZenRules() {
+ if (!android.app.Flags.modesApi()) {
+ throw new IllegalStateException("getAutomaticZenRules called with flag off!");
+ }
+ enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+ return mZenModeHelper.getAutomaticZenRules();
+ }
+
+ @Override
public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
@@ -11449,17 +11460,16 @@
}
}
}
- }
- // clean up anything in the disallowed pkgs list
- for (int i = 0; i < pkgList.length; i++) {
- String pkg = pkgList[i];
- for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
- NotificationListenerFilter nlf =
- mRequestedNotificationListeners.valueAt(j);
-
- VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
- nlf.removePackage(ai);
+ // Clean up removed package from the disallowed packages list
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ NotificationListenerFilter nlf =
+ mRequestedNotificationListeners.valueAt(j);
+ VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+ nlf.removePackage(ai);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 8f5676b3..9106c33 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -118,7 +118,10 @@
protected boolean canSnooze(int numberToSnooze) {
synchronized (mLock) {
- if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
+ if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT
+ || (mPersistedSnoozedNotifications.size()
+ + mPersistedSnoozedNotificationsWithContext.size() + numberToSnooze)
+ > CONCURRENT_SNOOZE_LIMIT) {
return false;
}
}
@@ -357,6 +360,9 @@
if (groupSummaryKey != null) {
NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey);
+ String trimmedKey = getTrimmedString(groupSummaryKey);
+ mPersistedSnoozedNotificationsWithContext.remove(trimmedKey);
+ mPersistedSnoozedNotifications.remove(trimmedKey);
if (record != null && !record.isCanceled) {
Runnable runnable = () -> {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index c637df2..a1704c6 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -106,7 +106,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -334,6 +336,7 @@
return mZenMode;
}
+ // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
synchronized (mConfigLock) {
@@ -347,6 +350,20 @@
return rules;
}
+ /**
+ * Get the list of {@link AutomaticZenRule} instances that the calling package can manage
+ * (which means the owned rules for a regular app, and every rule for system callers) together
+ * with their ids.
+ */
+ Map<String, AutomaticZenRule> getAutomaticZenRules() {
+ List<ZenRule> ruleList = getZenRules();
+ HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
+ for (ZenRule rule : ruleList) {
+ rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
+ }
+ return rules;
+ }
+
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
synchronized (mConfigLock) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 21284a0..659c36c 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -45,11 +45,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 21610c9..bdcec3a 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -58,6 +58,9 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
@@ -68,9 +71,6 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java
index d38b83f..f3f64c5 100644
--- a/services/core/java/com/android/server/pm/AppsFilterUtils.java
+++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java
@@ -27,15 +27,15 @@
import android.util.ArraySet;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedSparseSetArray;
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2617703..5e76ae5 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -123,6 +123,12 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -141,12 +147,6 @@
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f8d27f1..d46d559 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -154,6 +154,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -177,11 +182,6 @@
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.FileIntegrityService;
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index e749968..ea783b8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -47,13 +47,13 @@
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 434c00a..7d3d85d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,8 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -233,8 +235,6 @@
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.pkg.mutate.PackageStateWrite;
import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index bcb7bde..cd34163 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -92,6 +92,7 @@
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -104,7 +105,6 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java
index 241f143..651bc5f 100644
--- a/services/core/java/com/android/server/pm/PackageProperty.java
+++ b/services/core/java/com/android/server/pm/PackageProperty.java
@@ -31,8 +31,8 @@
import android.os.UserHandle;
import android.util.ArrayMap;
+import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedComponent;
import java.util.ArrayList;
import java.util.Iterator;
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index b055a3f..a8196f3 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -44,6 +44,7 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.parsing.PackageCacher;
@@ -52,7 +53,6 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import java.io.File;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 7ea9e3f..22ee963 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -73,6 +73,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -82,11 +87,6 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedArraySet;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6338965..7c969ef 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -89,6 +89,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -113,10 +117,6 @@
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.SuspendParams;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.resolution.ComponentResolver;
import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 9376259..dddc6b0 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -25,13 +25,13 @@
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProcessImpl;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
index adac68b..2156467 100644
--- a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
+++ b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
@@ -29,9 +29,9 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 38cde3e..91a70a6 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -52,6 +52,17 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageArchiver;
@@ -65,17 +76,6 @@
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentParseUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 97d526d..8916efd7 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -20,8 +20,8 @@
import android.annotation.Nullable;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedComponent;
/**
* For exposing internal fields to the rest of the server, enforcing that any overridden state from
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index e2acc17..0eb2bbd 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -29,16 +29,16 @@
import android.os.incremental.IncrementalManager;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 75dd67d..370d239 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -50,6 +50,19 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
@@ -61,27 +74,15 @@
import com.android.server.pm.pkg.AndroidPackageSplitImpl;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.pm.pkg.component.ParsedAttributionImpl;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedProcessImpl;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
@@ -3305,7 +3306,7 @@
this.instrumentations = ParsingUtils.createTypedInterfaceList(in,
ParsedInstrumentationImpl.CREATOR);
this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
- this.processes = in.readHashMap(ParsedProcess.class.getClassLoader());
+ this.processes = in.readHashMap(ParsedProcessImpl.class.getClassLoader());
this.metaData = in.readBundle(boot);
this.volumeUuid = sForInternedString.unparcel(in);
this.signingDetails = in.readParcelable(boot, android.content.pm.SigningDetails.class);
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index c81d6d7..07ff0ee 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -28,9 +28,9 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.pkg.PackageState;
-import com.android.server.pm.pkg.component.ParsedPermission;
import libcore.util.EmptyArray;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 6764e08..883b066 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -119,6 +119,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IntPair;
@@ -142,8 +144,6 @@
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
index 3a61704..61677eb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
@@ -18,10 +18,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+
import java.util.Collection;
/**
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 91854fd..4d4efac 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -47,17 +47,17 @@
import android.util.SparseIntArray;
import com.android.internal.R;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import java.security.PublicKey;
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index 1d2c5ec..20fcdb5 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -22,7 +22,7 @@
import android.content.pm.PackageManager;
import android.util.SparseArray;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
/** @hide */
public class PackageStateUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
index cd3583b..fe80f74 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java
@@ -27,7 +27,7 @@
import android.util.DebugUtils;
import android.util.Slog;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
/** @hide */
public class PackageUserStateUtils {
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
index 063f577..411bded 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -22,8 +22,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.server.pm.permission.LegacyPermissionState;
-import com.android.server.pm.pkg.component.ParsedProcess;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
index 1deb8d0..1964df0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java
@@ -19,6 +19,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+
/**
* Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
* be removed once all post-parsing mutation is moved to parsing.
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
index a8fb79a..041edaa 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
@@ -29,6 +29,9 @@
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 68d5428..f027901 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -36,7 +36,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.server.pm.pkg.parsing.ParsingUtils;
@@ -49,7 +49,6 @@
* @hide
**/
@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity,
Parcelable {
@@ -133,7 +132,7 @@
* should be invisible to user and user should not know or see it.
*/
@NonNull
- static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
+ public static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
ParsedActivityImpl activity = new ParsedActivityImpl();
activity.setPackageName(packageName);
@@ -700,7 +699,7 @@
time = 1669437519576L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
- inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+ inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index ee793c8..5709cbb 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -48,6 +48,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
index 167aba3..cfed19a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -22,6 +22,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -250,7 +251,7 @@
time = 1643723578605L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java",
- inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+ inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
index ed9aa2e..d3fb29b 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -25,6 +25,8 @@
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
index b59f511..62b9947 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java
@@ -22,6 +22,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import com.android.internal.util.DataClass;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
index 98e94c5..411220a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java
@@ -26,6 +26,7 @@
import android.util.ArraySet;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
index f8d678e..512e5c7 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java
@@ -32,6 +32,8 @@
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
index 8a0d356..7bfad14 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java
@@ -26,6 +26,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
index c63a689..9792a91 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -26,6 +26,7 @@
import android.content.res.XmlResourceParser;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
index 5b6375d..ab94043 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java
@@ -23,6 +23,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.DataClass;
/**
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
index 4f0a504..5e67bbf 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -31,6 +31,7 @@
import android.util.TypedValue;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
index c670e7c..f322eef 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java
@@ -25,6 +25,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
index f52ad13..6c22f82 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -31,6 +31,8 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
index 59075de..afe37bc 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java
@@ -21,6 +21,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.DataClass;
/**
@@ -174,7 +175,7 @@
time = 1642132854167L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java",
- inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.server.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+ inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
index 4c831d3..69e33c8 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -24,6 +24,8 @@
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -119,8 +121,8 @@
this.requestRes = in.readInt();
this.protectionLevel = in.readInt();
this.tree = in.readBoolean();
- this.parsedPermissionGroup = in.readParcelable(ParsedPermissionGroup.class.getClassLoader(),
- ParsedPermissionGroupImpl.class);
+ this.parsedPermissionGroup = in.readParcelable(
+ ParsedPermissionGroupImpl.class.getClassLoader(), ParsedPermissionGroupImpl.class);
this.knownCerts = sForStringSet.unparcel(in);
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
index c6d17753..0f2b49b 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -31,6 +31,8 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
index 6d52f65..40e3670 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java
@@ -25,7 +25,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -35,7 +35,6 @@
/** @hide */
@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
genBuilder = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class ParsedProcessImpl implements ParsedProcess, Parcelable {
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
index 4f4c2d5..766fb90 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -27,6 +27,7 @@
import android.util.ArraySet;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedProcess;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
index 6f4b4c8..81a3c17 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
@@ -28,6 +28,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
@@ -301,7 +302,7 @@
time = 1642560323360L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java",
- inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+ inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
index 37bed15..b66db4f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -34,6 +34,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
index 47e993c..ca8c45d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java
@@ -26,6 +26,8 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index c15266f..1b42184 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -32,6 +32,7 @@
import android.os.Build;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
index 9b89373..78377a8 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java
@@ -21,6 +21,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 699ccbd..408a531 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -32,18 +32,18 @@
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import java.security.PublicKey;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 061698a..417e3ae 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -89,6 +89,19 @@
import com.android.internal.R;
import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.SharedUidMigration;
@@ -98,29 +111,17 @@
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
-import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils;
-import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.pm.pkg.component.ParsedAttributionUtils;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
import com.android.server.pm.pkg.component.ParsedIntentInfoUtils;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProcessUtils;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderUtils;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceUtils;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.split.DefaultSplitAssetLoader;
import com.android.server.pm.split.SplitAssetDependencyLoader;
@@ -2842,7 +2843,7 @@
String taskAffinity = result.getResult();
// Build custom App Details activity info instead of parsing it from xml
- return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
+ return input.success(ParsedActivityImpl.makeAppDetailsActivity(packageName,
pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
pkg.isHardwareAccelerated()));
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 0751285..2cfffb3 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -30,9 +30,9 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.util.Parcelling;
import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 0ceda42..ed6d3b9 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -45,6 +45,12 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.Computer;
@@ -57,13 +63,7 @@
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
index b8e4c8d..0f12ee1 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -25,11 +25,11 @@
import android.content.pm.ResolveInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import java.io.PrintWriter;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 80cde73..2bc926c 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -28,6 +28,11 @@
import android.util.ArrayMap;
import android.util.Pair;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
import com.android.server.pm.UserManagerService;
@@ -36,11 +41,6 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.utils.WatchableImpl;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
index 0c84f4c..add33b2 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
@@ -24,13 +24,13 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
import com.android.server.pm.Computer;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerTracedLock;
import com.android.server.pm.UserManagerService;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
import java.io.PrintWriter;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index adef808..735f90f 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -27,11 +27,11 @@
import android.util.ArraySet;
import android.util.Patterns;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import java.util.List;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 3d4d4ec..6150099 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -48,6 +48,7 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.util.CollectionUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -60,7 +61,6 @@
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 77290fd..9f0a975 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -603,40 +603,51 @@
@NonNull
private String getEnergyConsumerName(EnergyConsumer consumer,
EnergyConsumer[] energyConsumers) {
- if (consumer.type != EnergyConsumerType.OTHER) {
- StringBuilder sb = new StringBuilder();
- sb.append(energyConsumerTypeToString(consumer.type));
- boolean hasOrdinal = consumer.ordinal != 0;
- if (!hasOrdinal) {
- // See if any other EnergyConsumer of the same type has an ordinal
- for (EnergyConsumer aConsumer : energyConsumers) {
- if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
- hasOrdinal = true;
- break;
- }
+ StringBuilder sb = new StringBuilder();
+ switch (consumer.type) {
+ case EnergyConsumerType.BLUETOOTH:
+ sb.append("BLUETOOTH");
+ break;
+ case EnergyConsumerType.CPU_CLUSTER:
+ sb.append("CPU");
+ break;
+ case EnergyConsumerType.DISPLAY:
+ sb.append("DISPLAY");
+ break;
+ case EnergyConsumerType.GNSS:
+ sb.append("GNSS");
+ break;
+ case EnergyConsumerType.MOBILE_RADIO:
+ sb.append("MOBILE_RADIO");
+ break;
+ case EnergyConsumerType.WIFI:
+ sb.append("WIFI");
+ break;
+ case EnergyConsumerType.CAMERA:
+ sb.append("CAMERA");
+ break;
+ default:
+ if (consumer.name != null && !consumer.name.isBlank()) {
+ sb.append(consumer.name.toUpperCase(Locale.ENGLISH));
+ } else {
+ sb.append("CONSUMER_").append(consumer.type);
+ }
+ break;
+ }
+ boolean hasOrdinal = consumer.ordinal != 0;
+ if (!hasOrdinal) {
+ // See if any other EnergyConsumer of the same type has an ordinal
+ for (EnergyConsumer aConsumer : energyConsumers) {
+ if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
+ hasOrdinal = true;
+ break;
}
}
- if (hasOrdinal) {
- sb.append('/').append(consumer.ordinal);
- }
- return sb.toString();
- } else {
- return consumer.name;
}
- }
-
- private static String energyConsumerTypeToString(int type) {
- switch(type) {
- case EnergyConsumerType.BLUETOOTH: return "BLUETOOTH";
- case EnergyConsumerType.CPU_CLUSTER: return "CPU";
- case EnergyConsumerType.DISPLAY: return "DISPLAY";
- case EnergyConsumerType.GNSS: return "GNSS";
- case EnergyConsumerType.MOBILE_RADIO: return "MOBILE_RADIO";
- case EnergyConsumerType.WIFI: return "WIFI";
- case EnergyConsumerType.OTHER: return "";
- default:
- throw new IllegalStateException("Unrecognized EnergyConsumerType: " + type);
+ if (hasOrdinal) {
+ sb.append('/').append(consumer.ordinal);
}
+ return sb.toString();
}
/**
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 6d580e9..8b57f87 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -280,12 +280,12 @@
return null;
}
- if (getSessionCountByUidLocked(callingUid) >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
+ if (getSessionCountByUidLocked(callingUid) == MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
Slog.w(TAG, "Number of sessions exceeded for uid: " + callingUid);
Counter.logIncrementWithUid(
"speech_recognition.value_exceed_session_count",
callingUid);
- return null;
+ // TODO(b/297249772): return null early to refuse the new connection
}
if (servicesForClient != null) {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index fed6e7e..b2e808a 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -117,16 +117,16 @@
static final class CallerInfo {
public final VibrationAttributes attrs;
public final int uid;
- public final int displayId;
+ public final int deviceId;
public final String opPkg;
public final String reason;
- CallerInfo(@NonNull VibrationAttributes attrs, int uid, int displayId,
- String opPkg, String reason) {
+ CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
+ String reason) {
Objects.requireNonNull(attrs);
this.attrs = attrs;
this.uid = uid;
- this.displayId = displayId;
+ this.deviceId = deviceId;
this.opPkg = opPkg;
this.reason = reason;
}
@@ -138,14 +138,14 @@
CallerInfo that = (CallerInfo) o;
return Objects.equals(attrs, that.attrs)
&& uid == that.uid
- && displayId == that.displayId
+ && deviceId == that.deviceId
&& Objects.equals(opPkg, that.opPkg)
&& Objects.equals(reason, that.reason);
}
@Override
public int hashCode() {
- return Objects.hash(attrs, uid, displayId, opPkg, reason);
+ return Objects.hash(attrs, uid, deviceId, opPkg, reason);
}
@Override
@@ -153,7 +153,7 @@
return "CallerInfo{"
+ " uid=" + uid
+ ", opPkg=" + opPkg
- + ", displayId=" + displayId
+ + ", deviceId=" + deviceId
+ ", attrs=" + attrs
+ ", reason=" + reason
+ '}';
@@ -267,8 +267,8 @@
mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
String callerInfoStr = String.format(Locale.ROOT,
- " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
- mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+ " | %s (uid=%d, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId,
mCallerInfo.attrs.usageToString(),
AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 7f55836..839c207 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -61,7 +61,6 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -166,7 +165,6 @@
final MyUidObserver mUidObserver;
@VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
- final VirtualDeviceListener mVirtualDeviceListener;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -180,6 +178,8 @@
@GuardedBy("mLock")
@Nullable
private PowerManagerInternal mPowerManagerInternal;
+ @Nullable
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
@GuardedBy("mLock")
private boolean mVibrateInputDevices;
@@ -207,8 +207,6 @@
mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new MyUidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
- mVirtualDeviceListener = new VirtualDeviceListener();
-
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -272,13 +270,6 @@
}
});
- VirtualDeviceManagerInternal vdm = LocalServices.getService(
- VirtualDeviceManagerInternal.class);
- if (vdm != null) {
- vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
- vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
- }
-
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
@@ -414,8 +405,14 @@
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
- if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(callerInfo.uid,
- callerInfo.displayId)) {
+
+ if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT
+ && callerInfo.deviceId != Context.DEVICE_ID_INVALID) {
+ return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ }
+
+ if (callerInfo.deviceId == Context.DEVICE_ID_INVALID
+ && isAppRunningOnAnyVirtualDevice(callerInfo.uid)) {
return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
@@ -794,6 +791,15 @@
return out;
}
+ private boolean isAppRunningOnAnyVirtualDevice(int uid) {
+ if (mVirtualDeviceManagerInternal == null) {
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ return mVirtualDeviceManagerInternal != null
+ && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(uid);
+ }
+
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
final class SettingsContentObserver extends ContentObserver {
@@ -853,73 +859,4 @@
}
}
}
-
- /**
- * Implementation of Virtual Device listeners for the changes of virtual displays and of apps
- * running on any virtual device.
- */
- final class VirtualDeviceListener implements
- VirtualDeviceManagerInternal.VirtualDisplayListener,
- VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener {
- @GuardedBy("mLock")
- private final Set<Integer> mVirtualDisplays = new HashSet<>();
- @GuardedBy("mLock")
- private final Set<Integer> mAppsOnVirtualDevice = new HashSet<>();
-
-
- @Override
- public void onVirtualDisplayCreated(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.add(displayId);
- }
- }
-
- @Override
- public void onVirtualDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.remove(displayId);
- }
- }
-
-
- @Override
- public void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids) {
- synchronized (mLock) {
- mAppsOnVirtualDevice.clear();
- mAppsOnVirtualDevice.addAll(allRunningUids);
- }
- }
-
- /**
- * @param uid: uid of the calling app.
- * @param displayId: the id of a Display.
- * @return Returns true if:
- * <ul>
- * <li> the displayId is valid, and it's owned by a virtual device.</li>
- * <li> the displayId is invalid, and the calling app (uid) is running on a virtual
- * device.</li>
- * </ul>
- */
- public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- // The default display is the primary physical display on the phone.
- return false;
- }
-
- synchronized (mLock) {
- if (displayId == Display.INVALID_DISPLAY) {
- // There is no Display object associated with the Context of calling
- // {@link SystemVibratorManager}, checking the calling UID instead.
- return mAppsOnVirtualDevice.contains(uid);
- } else {
- // Other valid display IDs representing valid logical displays will be
- // checked
- // against the active virtual displays set built with the registered
- // {@link VirtualDisplayListener}.
- return mVirtualDisplays.contains(displayId);
- }
- }
- }
-
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ace7777..cf33cc5 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -63,7 +63,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,7 +384,7 @@
return false;
}
AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
- new Vibration.CallerInfo(attrs, uid, Display.DEFAULT_DISPLAY, opPkg,
+ new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
null), effects);
mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
updateAlwaysOnLocked(alwaysOnVibration);
@@ -397,16 +396,16 @@
}
@Override // Binder call
- public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect,
+ public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- vibrateWithPermissionCheck(uid, displayId, opPkg, effect, attrs, reason, token);
+ vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
}
@Override // Binder call
public void performHapticFeedback(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
- performHapticFeedbackInternal(uid, displayId, opPkg, constant, always, reason, token);
+ performHapticFeedbackInternal(uid, deviceId, opPkg, constant, always, reason, token);
}
/**
@@ -417,7 +416,7 @@
@VisibleForTesting
@Nullable
HalVibration performHapticFeedbackInternal(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
if (hapticVibrationProvider == null) {
@@ -433,7 +432,7 @@
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
constant, /* bypassVibrationIntensitySetting= */ always);
- return vibrateWithoutPermissionCheck(uid, displayId, opPkg, combinedVibration, attrs,
+ return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
}
@@ -444,7 +443,7 @@
*/
@VisibleForTesting
@Nullable
- HalVibration vibrateWithPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
@@ -452,24 +451,24 @@
attrs = fixupVibrationAttributes(attrs, effect);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE, "vibrate");
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
try {
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
+ private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
if (token == null) {
@@ -482,7 +481,7 @@
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
HalVibration vib = new HalVibration(token, effect,
- new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
+ new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason));
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -1558,10 +1557,9 @@
private ExternalVibrationHolder(ExternalVibration externalVibration) {
super(externalVibration.getToken(), new Vibration.CallerInfo(
externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
- Display.INVALID_DISPLAY,
- externalVibration.getPackage(), null));
+ // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
+ // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
+ Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
this.externalVibration = externalVibration;
this.scale = IExternalVibratorService.SCALE_NONE;
mStatus = Vibration.Status.RUNNING;
@@ -1974,8 +1972,6 @@
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
synchronized (mLock) {
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
vibHolder.callerInfo);
@@ -2184,7 +2180,7 @@
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, combined, attrs,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
commonOptions.description, deathBinder);
maybeWaitOnVibration(vib, commonOptions);
}
@@ -2241,7 +2237,7 @@
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, constant,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
/* always= */ commonOptions.force, /* reason= */ commonOptions.description,
deathBinder);
maybeWaitOnVibration(vib, commonOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 26f0d34..7b399c8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1018,9 +1018,8 @@
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(r.app.getThread());
- transaction.addCallback(EnterPipRequestedItem.obtain(r.token));
- mService.getLifecycleManager().scheduleTransaction(transaction);
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
+ EnterPipRequestedItem.obtain(r.token));
return true;
} catch (Exception e) {
Slog.w(TAG, "Failed to send enter pip requested item: "
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index eeeca10..24d9938 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8185,6 +8185,12 @@
* aspect ratio.
*/
boolean shouldCreateCompatDisplayInsets() {
+ if (mLetterboxUiController.shouldApplyUserFullscreenOverride()) {
+ // If the user has forced the applications aspect ratio to be fullscreen, don't use size
+ // compatibility mode in any situation. The user has been warned and therefore accepts
+ // the risk of the application misbehaving.
+ return false;
+ }
switch (supportsSizeChanges()) {
case SIZE_CHANGES_SUPPORTED_METADATA:
case SIZE_CHANGES_SUPPORTED_OVERRIDE:
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 777b5cd..e196d46 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -936,7 +936,7 @@
final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
clientTransaction.addCallback(LaunchActivityItem.obtain(r.token,
- new Intent(r.intent), System.identityHashCode(r), r.info,
+ r.intent, System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index a2f5a38..c2b5f88 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -583,7 +583,7 @@
+ " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalBlockedToast("BAL would be blocked", state);
+ showBalRiskToast("BAL would be blocked", state);
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 735cbc4..5518de7 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -254,7 +254,9 @@
// Counter for ActivityRecord#setRequestedOrientation
private int mSetOrientationRequestCounter = 0;
- // The min aspect ratio override set by user
+ // The min aspect ratio override set by user. Stores the last selected aspect ratio after
+ // {@link #shouldApplyUserFullscreenOverride} or {@link #shouldApplyUserMinAspectRatioOverride}
+ // have been invoked.
@PackageManager.UserMinAspectRatio
private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
@@ -661,7 +663,9 @@
@ScreenOrientation
int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
- if (shouldApplyUserFullscreenOverride()) {
+ if (shouldApplyUserFullscreenOverride()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -1171,9 +1175,7 @@
boolean shouldApplyUserFullscreenOverride() {
if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
|| FALSE.equals(mBooleanPropertyAllowUserAspectRatioFullscreenOverride)
- || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()
- || mActivityRecord.mDisplayContent == null
- || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) {
return false;
}
diff --git a/services/proguard.flags b/services/proguard.flags
index 261bb7c..407505d 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -14,13 +14,20 @@
}
# APIs referenced by dependent JAR files and modules
--keep @interface android.annotation.SystemApi
+# TODO(b/300514883): Pull @SystemApi keep rules from system-api.pro.
+-keep interface android.annotation.SystemApi
-keep @android.annotation.SystemApi class * {
public protected *;
}
-keepclasseswithmembers class * {
@android.annotation.SystemApi *;
}
+# Also ensure nested classes are kept. This is overly conservative, but handles
+# cases where such classes aren't explicitly marked @SystemApi.
+-if @android.annotation.SystemApi class *
+-keep public class <1>$** {
+ public protected *;
+}
# Derivatives of SystemService and other services created via reflection
-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 12cd0f6..8d76fdd 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -24,6 +24,7 @@
import android.os.Binder
import android.os.UserHandle
import android.util.ArrayMap
+import com.android.internal.pm.pkg.component.ParsedActivity
import com.android.server.pm.AppsFilterImpl
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageManagerServiceInjector
@@ -39,7 +40,6 @@
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.component.ParsedActivity
import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.snapshot.PackageDataSnapshot
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index d5cd6ef9..25146a8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -49,16 +49,16 @@
import androidx.annotation.NonNull;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
import com.android.server.pm.pkg.component.ParsedComponentImpl;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 0eac4e6..7c28e13 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,16 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedApexSystemService;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -69,24 +79,14 @@
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedPermission;
-import com.android.server.pm.pkg.component.ParsedPermissionGroup;
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl;
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
-import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
-import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
-import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
import com.android.server.pm.pkg.parsing.ParsingPackage;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 9e37164..7123c20 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,16 +38,16 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.pm.test.service.server.R;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
+import com.android.server.pm.test.service.server.R;
import com.google.common.truth.Expect;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 0e2e35f..2646854 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -17,7 +17,7 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.ActivityInfo
-import com.android.server.pm.pkg.component.ParsedActivity
+import com.android.internal.pm.pkg.component.ParsedActivity
import com.android.server.pm.pkg.component.ParsedActivityImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
index 4e44e96..52d5b3b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedAttribution
+import com.android.internal.pm.pkg.component.ParsedAttribution
import com.android.server.pm.pkg.component.ParsedAttributionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
index 058f6d6..af0c0de 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -17,7 +17,7 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.PackageManager
-import com.android.server.pm.pkg.component.ParsedComponent
+import com.android.internal.pm.pkg.component.ParsedComponent
import com.android.server.pm.pkg.component.ParsedComponentImpl
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
import android.os.Bundle
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
index eeb30b7..dc0f194 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedInstrumentation
+import com.android.internal.pm.pkg.component.ParsedInstrumentation
import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
index f27a51f..5224f23 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedIntentInfo
+import com.android.internal.pm.pkg.component.ParsedIntentInfo
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
import android.os.Parcelable
import android.os.PatternMatcher
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
index a0d8c44..dfff602 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedMainComponent
+import com.android.internal.pm.pkg.component.ParsedMainComponent
import com.android.server.pm.pkg.component.ParsedMainComponentImpl
import android.os.Parcelable
import java.util.Arrays
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index f266e76..ccbf558 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
index c72a44e..2814783 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -16,8 +16,8 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
import com.android.server.pm.pkg.component.ParsedPermissionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 8b9361a..2e96046 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProcess
import com.android.server.pm.pkg.component.ParsedProcessImpl
import android.util.ArrayMap
import kotlin.contracts.ExperimentalContracts
@@ -45,7 +45,7 @@
override fun extraParams() = listOf(
getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")),
getter(ParsedProcess::getAppClassNamesByPackage, ArrayMap<String, String>().apply {
- put("package1", "classname1");
+ put("package1", "classname1")
}),
)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 0302d57..290dbd6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -17,14 +17,14 @@
package com.android.server.pm.test.parsing.parcelling
import android.content.pm.PathPermission
-import com.android.server.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedProvider
import com.android.server.pm.pkg.component.ParsedProviderImpl
import android.os.PatternMatcher
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
-class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class) {
-
+class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class)
+{
override val defaultImpl =
ParsedProviderImpl()
override val creator = ParsedProviderImpl.CREATOR
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index e2c9439..3ae7e92 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedService
+import com.android.internal.pm.pkg.component.ParsedService
import com.android.server.pm.pkg.component.ParsedServiceImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
index ad60736..67dfc6d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -16,7 +16,7 @@
package com.android.server.pm.test.parsing.parcelling
-import com.android.server.pm.pkg.component.ParsedUsesPermission
+import com.android.internal.pm.pkg.component.ParsedUsesPermission
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
import kotlin.contracts.ExperimentalContracts
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
index d217d63..1da3a22 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
@@ -24,26 +24,25 @@
import android.content.pm.VersionedPackage
import android.os.PatternMatcher
import android.util.ArraySet
+import com.android.internal.pm.pkg.component.ParsedActivity
+import com.android.internal.pm.pkg.component.ParsedInstrumentation
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
+import com.android.internal.pm.pkg.component.ParsedProcess
+import com.android.internal.pm.pkg.component.ParsedProvider
+import com.android.internal.pm.pkg.component.ParsedService
import com.android.server.pm.PackageSetting
import com.android.server.pm.PackageSettingBuilder
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.PackageUserStateImpl
-import com.android.server.pm.pkg.component.ParsedActivity
import com.android.server.pm.pkg.component.ParsedActivityImpl
import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedInstrumentation
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.pm.pkg.component.ParsedPermissionImpl
-import com.android.server.pm.pkg.component.ParsedProcess
import com.android.server.pm.pkg.component.ParsedProcessImpl
-import com.android.server.pm.pkg.component.ParsedProvider
import com.android.server.pm.pkg.component.ParsedProviderImpl
-import com.android.server.pm.pkg.component.ParsedService
import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest
import com.google.common.truth.Expect
import org.junit.Rule
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
index ec84bc3..316f338 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
@@ -26,6 +26,8 @@
import android.util.ArrayMap
import android.util.SparseArray
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.pm.pkg.component.ParsedPermission
+import com.android.internal.pm.pkg.component.ParsedPermissionGroup
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.permission.access.MutableAccessState
@@ -39,8 +41,6 @@
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 6a95d5c..499e700 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -229,7 +229,19 @@
LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight(),
0, APP_MODE_65.getRefreshRate())),
- /*displayResolutionRangeVotingEnabled*/ true}});
+ /*displayResolutionRangeVotingEnabled*/ true},
+ {/*expectedBaseModeId*/ APP_MODE_65.getModeId(),
+ /*expectedPhysicalRefreshRate*/ 64.99f,
+ /*expectedAppRequestedRefreshRate*/ 64.99f,
+ /*votesWithPriorities*/ Map.of(
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forPhysicalRefreshRates(
+ 0, 64.99f))}});
final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index df46054..1838fe8 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -1081,7 +1081,7 @@
assertThat(result.powerMonitors).isNotNull();
assertThat(Arrays.stream(result.powerMonitors).map(PowerMonitor::getName).toList())
.containsAtLeast(
- "energyconsumer0",
+ "ENERGYCONSUMER0",
"BLUETOOTH/1",
"[channelname0]:channelsubsystem0",
"[channelname1]:channelsubsystem1");
@@ -1131,7 +1131,7 @@
Map<String, PowerMonitor> map =
Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
.collect(Collectors.toMap(PowerMonitor::getName, pm -> pm));
- PowerMonitor consumer1 = map.get("energyconsumer0");
+ PowerMonitor consumer1 = map.get("ENERGYCONSUMER0");
PowerMonitor consumer2 = map.get("BLUETOOTH/1");
PowerMonitor measurement1 = map.get("[channelname0]:channelsubsystem0");
PowerMonitor measurement2 = map.get("[channelname1]:channelsubsystem1");
@@ -1196,6 +1196,6 @@
supportedPowerMonitorsResult = new GetSupportedPowerMonitorsResult();
mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult);
assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
- .map(PowerMonitor::getName).toList()).contains("energyconsumer0");
+ .map(PowerMonitor::getName).toList()).contains("ENERGYCONSUMER0");
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2598a6b..f978990 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -52,9 +52,11 @@
import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
+import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -134,6 +136,8 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -259,10 +263,10 @@
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
- private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
- @Mock
private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener;
@Mock
+ private Consumer<String> mPersistentDeviceIdRemovedListener;
+ @Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -374,9 +378,8 @@
mCameraAccessController =
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
- mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null,
- null, MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false,
- 0, 0, -1);
+ mAssociationInfo = createAssociationInfo(
+ /* associationId= */ 1, AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
@@ -724,25 +727,36 @@
}
@Test
- public void onVirtualDisplayCreatedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
-
- mLocalService.onVirtualDisplayCreated(DISPLAY_ID_1);
+ public void onPersistentDeviceIdsRemoved_listenersNotified() {
+ mLocalService.registerPersistentDeviceIdRemovedListener(mPersistentDeviceIdRemovedListener);
+ mLocalService.onPersistentDeviceIdsRemoved(Set.of(mDeviceImpl.getPersistentDeviceId()));
TestableLooper.get(this).processAllMessages();
- verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID_1);
+ verify(mPersistentDeviceIdRemovedListener).accept(mDeviceImpl.getPersistentDeviceId());
}
@Test
- public void onVirtualDisplayRemovedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
-
- addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-
- mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
+ public void onCdmAssociationsChanged_persistentDeviceIdRemovedListenersNotified() {
+ mLocalService.registerPersistentDeviceIdRemovedListener(mPersistentDeviceIdRemovedListener);
+ mVdms.onCdmAssociationsChanged(List.of(mAssociationInfo));
TestableLooper.get(this).processAllMessages();
- verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID_1);
+ mVdms.onCdmAssociationsChanged(List.of(
+ createAssociationInfo(2, AssociationRequest.DEVICE_PROFILE_APP_STREAMING),
+ createAssociationInfo(3, AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION),
+ createAssociationInfo(4, AssociationRequest.DEVICE_PROFILE_WATCH)));
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mPersistentDeviceIdRemovedListener).accept(mDeviceImpl.getPersistentDeviceId());
+
+ mVdms.onCdmAssociationsChanged(Collections.emptyList());
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mPersistentDeviceIdRemovedListener)
+ .accept(VirtualDeviceImpl.createPersistentDeviceId(2));
+ verify(mPersistentDeviceIdRemovedListener)
+ .accept(VirtualDeviceImpl.createPersistentDeviceId(3));
+ verifyNoMoreInteractions(mPersistentDeviceIdRemovedListener);
}
@Test
@@ -1884,11 +1898,16 @@
@Test
public void getPersistentIdForDevice_invalidDeviceId_returnsNull() {
assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_INVALID)).isNull();
- assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_DEFAULT)).isNull();
assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_2)).isNull();
}
@Test
+ public void getPersistentIdForDevice_defaultDeviceId() {
+ assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_DEFAULT)).isEqualTo(
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
public void getPersistentIdForDevice_returnsCorrectId() {
assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_1))
.isEqualTo(mDeviceImpl.getPersistentDeviceId());
@@ -1943,6 +1962,14 @@
return intent.resolveActivity(packageManager);
}
+ private AssociationInfo createAssociationInfo(int associationId, String deviceProfile) {
+ return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
+ /* tag= */ null, MacAddress.BROADCAST_ADDRESS, /* displayName= */ "", deviceProfile,
+ /* associatedDevice= */ null, /* selfManaged= */ true,
+ /* notifyOnDeviceNearby= */ false, /* revoked= */false, /* timeApprovedMs= */0,
+ /* lastTimeConnectedMs= */0, /* systemDataSyncFlags= */ -1);
+ }
+
/** Helper class to drop permissions temporarily and restore them at the end of a test. */
static final class DropShellPermissionsTemporarily implements AutoCloseable {
DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index c632727f..9e5bea7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -1680,4 +1680,47 @@
assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
}
+
+ @Test
+ public void onAddressAllocated_startRequestActiveSourceAction_playbackActiveSource() {
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, 0x1000);
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.clearResultMessages();
+ mNativeWrapper.onCecMessage(activeSourceFromPlayback);
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+ }
+
+ @Test
+ public void onAddressAllocated_startRequestActiveSourceAction_noActiveSource() {
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 4406d83..ea11395 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -426,7 +426,7 @@
}
@Test
- public void testOnPackageChanged_removingDisallowedPackage() {
+ public void testOnPackageChanged_removingPackage_removeFromDisallowed() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
@@ -440,6 +440,25 @@
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
.getDisallowedPackages()).isEmpty();
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).isEmpty();
+ }
+
+ @Test
+ public void testOnPackageChanged_notRemovingPackage_staysInDisallowed() {
+ NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ NotificationListenerFilter nlf2 =
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+ mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+ mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+ String[] pkgs = new String[] {"pkg1"};
+ int[] uids = new int[] {243};
+ mListeners.onPackagesChanged(false, pkgs, uids);
+
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).contains(a1);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 47f15b8..1e3b728 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -19,6 +19,7 @@
import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -73,6 +74,14 @@
public class SnoozeHelperTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String XML_TAG_NAME = "snoozed-notifications";
+ private static final String XML_SNOOZED_NOTIFICATION = "notification";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+ private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+ private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+ private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
@Mock SnoozeHelper.Callback mCallback;
@Mock AlarmManager mAm;
@Mock ManagedServices.UserProfiles mUserProfiles;
@@ -316,6 +325,53 @@
}
@Test
+ public void testSnoozeLimit_maximumPersisted() throws XmlPullParserException, IOException {
+ final long snoozeTimeout = 1234;
+ final String snoozeContext = "ctx";
+ // Serialize & deserialize notifications so that only persisted lists are used
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_NAME);
+ // Serialize maximum number of timed + context snoozed notifications, half of each
+ for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++) {
+ final boolean timedNotification = i % 2 == 0;
+ if (timedNotification) {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ serializer.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, 1);
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, "key" + i);
+ if (timedNotification) {
+ serializer.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME, snoozeTimeout);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID, snoozeContext);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ }
+ serializer.endTag(null, XML_TAG_NAME);
+ serializer.endDocument();
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser, 1);
+ // Verify that we can't snooze any more notifications
+ // and that the limit is caused by persisted notifications
+ assertThat(mSnoozeHelper.canSnooze(1)).isFalse();
+ assertThat(mSnoozeHelper.isSnoozed(UserHandle.USER_SYSTEM, "pkg", "key0")).isFalse();
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", "key0")).isEqualTo(snoozeTimeout);
+ assertThat(
+ mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ "key1")).isEqualTo(snoozeContext);
+ }
+
+ @Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -611,6 +667,7 @@
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
+ final int snoozeDuration = 1000;
IntArray profileIds = new IntArray();
profileIds.add(UserHandle.USER_SYSTEM);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
@@ -618,10 +675,14 @@
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(
"pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
- mSnoozeHelper.snooze(r, 1000);
- mSnoozeHelper.snooze(r2, 1000);
+ final long snoozeTime = System.currentTimeMillis() + snoozeDuration;
+ mSnoozeHelper.snooze(r, snoozeDuration);
+ mSnoozeHelper.snooze(r2, snoozeDuration);
assertEquals(2, mSnoozeHelper.getSnoozed().size());
assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isAtLeast(snoozeTime);
mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
@@ -630,6 +691,39 @@
assertEquals(1, mSnoozeHelper.getSnoozed().size());
assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isEqualTo(0);
+ }
+
+ @Test
+ public void snoozeWithContext_repostGroupSummary_removesPersisted() throws Exception {
+ final String snoozeContext = "zzzzz";
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ NotificationRecord r = getNotificationRecord(
+ "pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
+ NotificationRecord r2 = getNotificationRecord(
+ "pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
+ mSnoozeHelper.snooze(r, snoozeContext);
+ mSnoozeHelper.snooze(r2, snoozeContext);
+ assertEquals(2, mSnoozeHelper.getSnoozed().size());
+ assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isEqualTo(snoozeContext);
+
+ mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
+
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
+ verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r2, false);
+
+ assertEquals(1, mSnoozeHelper.getSnoozed().size());
+ assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isNull();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
new file mode 100644
index 0000000..8dcf89b
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.server.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.service.notification.ZenDeviceEffects;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ZenDeviceEffectsTest extends UiServiceTestCase {
+
+ @Test
+ public void builder() {
+ ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTapToWake(true).setShouldDisableTapToWake(false)
+ .setShouldDisableTiltToWake(true)
+ .setShouldMaximizeDoze(true)
+ .setShouldUseNightMode(false)
+ .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
+ assertThat(deviceEffects.shouldDisableAutoBrightness()).isFalse();
+ assertThat(deviceEffects.shouldDisableTapToWake()).isFalse();
+ assertThat(deviceEffects.shouldDisableTiltToWake()).isTrue();
+ assertThat(deviceEffects.shouldDisableTouch()).isFalse();
+ assertThat(deviceEffects.shouldDisplayGrayscale()).isFalse();
+ assertThat(deviceEffects.shouldMaximizeDoze()).isTrue();
+ assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
+ assertThat(deviceEffects.shouldUseNightMode()).isFalse();
+ assertThat(deviceEffects.shouldSuppressAmbientDisplay()).isTrue();
+ }
+
+ @Test
+ public void builder_fromInstance() {
+ ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldUseNightMode(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ ZenDeviceEffects modified = new ZenDeviceEffects.Builder(original)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(false)
+ .build();
+
+ assertThat(modified.shouldDimWallpaper()).isTrue(); // from original
+ assertThat(modified.shouldDisableTiltToWake()).isTrue(); // from original
+ assertThat(modified.shouldDisplayGrayscale()).isTrue(); // updated
+ assertThat(modified.shouldUseNightMode()).isFalse(); // updated
+ assertThat(modified.shouldSuppressAmbientDisplay()).isTrue(); // from original
+ }
+
+ @Test
+ public void writeToParcel_parcelsAndUnparcels() {
+ ZenDeviceEffects source = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldUseNightMode(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ ZenDeviceEffects copy;
+ try {
+ source.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ copy = ZenDeviceEffects.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+
+ assertThat(copy.shouldDimWallpaper()).isTrue();
+ assertThat(copy.shouldDisableTouch()).isTrue();
+ assertThat(copy.shouldMinimizeRadioUsage()).isTrue();
+ assertThat(copy.shouldUseNightMode()).isTrue();
+ assertThat(copy.shouldSuppressAmbientDisplay()).isTrue();
+ assertThat(copy.shouldDisplayGrayscale()).isFalse();
+ }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 7a2bb5a..f080341 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -75,8 +75,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.util.ArraySet;
-import android.view.Display;
import androidx.test.InstrumentationRegistry;
@@ -103,7 +101,7 @@
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int UID = 1;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -137,9 +135,6 @@
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private BroadcastReceiver mRegisteredBatteryBroadcastReceiver;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
@Before
public void setUp() throws Exception {
@@ -155,14 +150,6 @@
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
removeServicesForTest();
addServicesForTest();
@@ -654,62 +641,20 @@
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
- // Vibrations from the primary display is never ignored regardless of the creation and
- // removal of virtual displays and of the changes of apps running on virtual displays.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
+ public void shouldIgnoreVibrationFromVirtualDevices_defaultDevice_neverIgnored() {
+ // Vibrations from the primary device is never ignored.
for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ assertVibrationNotIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT);
}
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayVirtual() {
- // Ignore the vibration when the coming display id represents a virtual display.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
-
+ public void shouldIgnoreVibrationFromVirtualDevices_virtualDevice_alwaysIgnored() {
+ // Ignore the vibration when the coming device id represents a virtual device.
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID,
+ assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID,
Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
}
-
- // Stop ignoring when the virtual display is removed.
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID);
- }
- }
-
-
- @Test
- public void shouldIgnoreVibrationFromVirtualDisplays_appsOnVirtualDisplay() {
- // Ignore when the passed-in display id is invalid and the calling uid is on a virtual
- // display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY,
- Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
- }
-
- // Stop ignoring when the app is no longer on virtual display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY);
- }
-
}
@Test
@@ -932,13 +877,13 @@
private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
Vibration.Status expectedStatus) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY, expectedStatus);
+ assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus);
}
- private void assertVibrationIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId, Vibration.Status expectedStatus) {
+ private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId, Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
- VibrationAttributes.createForUsage(usage), UID, displayId, null, null);
+ VibrationAttributes.createForUsage(usage), UID, deviceId, null, null);
assertEquals(errorMessageForUsage(usage), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -946,7 +891,7 @@
private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertEquals(errorMessageForAttributes(attrs), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -957,27 +902,27 @@
private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
@VibrationAttributes.Flag int flags) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, Display.DEFAULT_DISPLAY, flags);
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, Context.DEVICE_ID_DEFAULT, flags);
}
- private void assertVibrationNotIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, displayId, /* flags= */ 0);
+ private void assertVibrationNotIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId) {
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, deviceId, /* flags= */ 0);
}
- private void assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(
- @VibrationAttributes.Usage int usage, int displayId,
+ private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice(
+ @VibrationAttributes.Usage int usage, int deviceId,
@VibrationAttributes.Flag int flags) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
- displayId, null, null);
+ deviceId, null, null);
assertNull(errorMessageForUsage(usage),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertNull(errorMessageForAttributes(attrs),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -1032,7 +977,7 @@
private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
@VibrationAttributes.Usage int usage) {
VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
- return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DISPLAY_ID, opPkg, null);
+ return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
}
private void setBatteryReceiverRegistrationResult(Intent result) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 085241f..b0aef47 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -88,7 +88,7 @@
private static final int TEST_TIMEOUT_MILLIS = 900;
private static final int UID = Process.ROOT_UID;
- private static final int DISPLAY_ID = 10;
+ private static final int DEVICE_ID = 10;
private static final int VIBRATOR_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
@@ -250,7 +250,7 @@
Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
- 1, /* displayId= */ -1, /* opPkg= */ null, /* reason= */ null));
+ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
mVibrationConductor.notifyCancelled(
cancelVibrationInfo,
/* immediate= */ false);
@@ -1641,7 +1641,7 @@
private HalVibration createVibration(CombinedVibration effect) {
return new HalVibration(mVibrationToken, effect,
- new Vibration.CallerInfo(ATTRS, UID, DISPLAY_ID, PACKAGE_NAME, "reason"));
+ new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
}
private SparseArray<VibratorController> createVibratorControllers() {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 3dfaed6..3fce9e7 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -84,10 +84,8 @@
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.flags.Flags;
@@ -129,7 +127,7 @@
// be cancelled in the body of the individual test.
private static final int CLEANUP_TIMEOUT_MILLIS = 100;
private static final int UID = Process.ROOT_UID;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -190,9 +188,6 @@
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
private VibrationConfig mVibrationConfig;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
private InputManager mInputManager;
@@ -223,14 +218,6 @@
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
@@ -273,6 +260,7 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
// Ignore potential exceptions about the looper having never dispatched any messages.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
if (mInputManagerGlobalSession != null) {
@@ -1510,65 +1498,33 @@
}
@Test
- public void vibrate_withVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
+ public void vibrate_ignoreVibrationFromVirtualDevice() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ VIRTUAL_DEVICE_ID,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback ignored when it's from a virtual display.
+ // Haptic feedback ignored when it's from a virtual device.
assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ Context.DEVICE_ID_DEFAULT,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the virtual display is removed.
+ // Haptic feedback played normally when it's from the default device.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
cancelVibrate(service); // Clean up long-ish effect.
}
@Test
- public void vibrate_withAppsOnVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
- mockVibrators(1);
- VibratorManagerService service = createSystemReadyService();
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
-
- // Haptic feedback ignored when it's from an app running virtual display.
- assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the same app no long runs on a virtual display.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- cancelVibrate(service); // Clean up long-ish effect.
- }
-
- @Test
public void vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined()
throws Exception {
mockVibrators(1);
@@ -1685,7 +1641,7 @@
}
@Test
- public void onExternalVibration_ignoreVibrationFromVirtualDevices() throws Exception {
+ public void onExternalVibration_ignoreVibrationFromVirtualDevices() {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
createSystemReadyService();
@@ -1697,8 +1653,8 @@
int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
+ when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
+ .thenReturn(true);
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
}
@@ -2396,7 +2352,7 @@
private HalVibration performHapticFeedbackAndWaitUntilFinished(VibratorManagerService service,
int constant, boolean always) throws InterruptedException {
HalVibration vib =
- service.performHapticFeedbackInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.performHapticFeedbackInternal(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
constant, always, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2414,7 +2370,7 @@
private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
HalVibration vib =
- service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.vibrateWithPermissionCheck(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
effect, attrs, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2430,12 +2386,12 @@
private void vibrate(VibratorManagerService service, CombinedVibration effect,
VibrationAttributes attrs) {
- vibrateWithDisplay(service, Display.DEFAULT_DISPLAY, effect, attrs);
+ vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
}
- private void vibrateWithDisplay(VibratorManagerService service, int displayId,
+ private void vibrateWithDevice(VibratorManagerService service, int deviceId,
CombinedVibration effect, VibrationAttributes attrs) {
- service.vibrate(UID, displayId, PACKAGE_NAME, effect, attrs, "some reason", service);
+ service.vibrate(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service);
}
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0c996e0..1776ba5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -195,6 +195,8 @@
setBooted(mAtm);
// Because the booted state is set, avoid starting real home if there is no task.
doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any());
+ // Do not execute the transaction, because we can't verify the parameter after it recycles.
+ doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
@@ -262,7 +264,7 @@
pauseFound.value = true;
}
return null;
- }).when(activity.app.getThread()).scheduleTransaction(any());
+ }).when(mClientLifecycleManager).scheduleTransaction(any());
activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
@@ -477,7 +479,7 @@
.build();
final Task task = activity.getTask();
activity.setState(DESTROYED, "Testing");
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
final Configuration newConfig = new Configuration(task.getConfiguration());
newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
@@ -487,7 +489,7 @@
ensureActivityConfiguration(activity);
- verify(mAtm.getLifecycleManager(), never())
+ verify(mClientLifecycleManager, never())
.scheduleTransaction(any(), isA(ActivityConfigurationChangeItem.class));
}
@@ -500,7 +502,7 @@
// test properly.
activity.finishRelaunching();
// Clear out any calls to scheduleTransaction from launching the activity.
- reset(mAtm.getLifecycleManager());
+ reset(mClientLifecycleManager);
final Task task = activity.getTask();
activity.setState(RESUMED, "Testing");
@@ -517,7 +519,7 @@
// The configuration change is still sent to the activity, even if it doesn't relaunch.
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(
+ verify(mClientLifecycleManager).scheduleTransaction(
eq(activity.app.getThread()), eq(expected));
}
@@ -558,19 +560,7 @@
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
activity.getConfiguration()));
- clearInvocations(mAtm.getLifecycleManager());
- final Configuration newConfig = new Configuration(activity.getConfiguration());
- final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
- final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
- if (newConfig.orientation == ORIENTATION_PORTRAIT) {
- newConfig.orientation = ORIENTATION_LANDSCAPE;
- newConfig.screenWidthDp = longSide;
- newConfig.screenHeightDp = shortSide;
- } else {
- newConfig.orientation = ORIENTATION_PORTRAIT;
- newConfig.screenWidthDp = shortSide;
- newConfig.screenHeightDp = longSide;
- }
+ clearInvocations(mClientLifecycleManager);
// Mimic the behavior that display doesn't handle app's requested orientation.
final DisplayContent dc = activity.getTask().getDisplayContent();
@@ -578,12 +568,15 @@
doReturn(false).when(dc).handlesOrientationChangeFromDescendant(anyInt());
final int requestedOrientation;
- switch (newConfig.orientation) {
- case ORIENTATION_LANDSCAPE:
- requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
- break;
+ final int expectedOrientation;
+ switch (activity.getConfiguration().orientation) {
case ORIENTATION_PORTRAIT:
+ requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ expectedOrientation = ORIENTATION_LANDSCAPE;
+ break;
+ case ORIENTATION_LANDSCAPE:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ expectedOrientation = ORIENTATION_PORTRAIT;
break;
default:
throw new IllegalStateException("Orientation in new config should be either"
@@ -595,11 +588,11 @@
activity.setRequestedOrientation(requestedOrientation);
+ final Configuration currentConfig = activity.getConfiguration();
+ assertEquals(expectedOrientation, currentConfig.orientation);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
- eq(expected));
-
+ ActivityConfigurationChangeItem.obtain(activity.token, currentConfig);
+ verify(mClientLifecycleManager).scheduleTransaction(activity.app.getThread(), expected);
verify(displayRotation).onSetRequestedOrientation();
}
@@ -788,7 +781,7 @@
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
doReturn(false).when(stack).isTranslucent(any());
assertTrue(task.shouldBeVisible(null /* starting */));
@@ -796,7 +789,10 @@
activity.getConfiguration()));
final Configuration newConfig = new Configuration(activity.getConfiguration());
- final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int shortSide = newConfig.screenWidthDp == newConfig.screenHeightDp
+ // To avoid the case where it is always portrait because of width == height.
+ ? newConfig.screenWidthDp - 1
+ : Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
if (newConfig.orientation == ORIENTATION_PORTRAIT) {
newConfig.orientation = ORIENTATION_LANDSCAPE;
@@ -811,12 +807,12 @@
task.onConfigurationChanged(newConfig);
activity.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */, true /* ignoreStopState */);
+ false /* preserveWindow */, true /* ignoreVisibility */);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(
- eq(activity.app.getThread()), eq(expected));
+ ActivityConfigurationChangeItem.obtain(activity.token,
+ activity.getConfiguration());
+ verify(mClientLifecycleManager).scheduleTransaction(activity.app.getThread(), expected);
} finally {
stack.getDisplayArea().removeChild(stack);
}
@@ -1259,12 +1255,12 @@
targetActivity.resultTo = sourceActivity;
targetActivity.setForceSendResultForMediaProjection();
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
try {
- verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
+ verify(mClientLifecycleManager, atLeastOnce()).scheduleTransaction(
any(ClientTransaction.class));
} catch (RemoteException ignored) {
}
@@ -1283,7 +1279,7 @@
targetActivity.setState(RESUMED, "test");
targetActivity.resultTo = resultToActivity;
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
waitUntilHandlersIdle();
@@ -1786,10 +1782,10 @@
final ActivityRecord activity = createActivityWithTask();
final WindowProcessController wpc = activity.app;
setup.accept(activity);
- clearInvocations(mAtm.getLifecycleManager());
+ clearInvocations(mClientLifecycleManager);
activity.getTask().removeImmediately("test");
try {
- verify(mAtm.getLifecycleManager()).scheduleTransaction(any(),
+ verify(mClientLifecycleManager).scheduleTransaction(any(),
isA(DestroyActivityItem.class));
} catch (RemoteException ignored) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e7ebd7db..3c027ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -39,9 +39,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.never;
@@ -51,7 +52,7 @@
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
-import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.EnterPipRequestedItem;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -90,9 +91,6 @@
@RunWith(WindowTestRunner.class)
public class ActivityTaskManagerServiceTests extends WindowTestsBase {
- private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
- ArgumentCaptor.forClass(ClientTransaction.class);
-
private static final String DEFAULT_PACKAGE_NAME = "my.application.package";
private static final int DEFAULT_USER_ID = 100;
@@ -123,53 +121,42 @@
final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
doReturn(mockLifecycleManager).when(mAtm).getLifecycleManager();
doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
+ final ArgumentCaptor<ClientTransactionItem> clientTransactionItemCaptor =
+ ArgumentCaptor.forClass(ClientTransactionItem.class);
+ verify(mockLifecycleManager).scheduleTransaction(any(),
+ clientTransactionItemCaptor.capture());
+ final ClientTransactionItem transactionItem = clientTransactionItemCaptor.getValue();
// Check that only an enter pip request item callback was scheduled.
- assertEquals(1, transaction.getCallbacks().size());
- assertTrue(transaction.getCallbacks().get(0) instanceof EnterPipRequestedItem);
- // Check the activity lifecycle state remains unchanged.
- assertNull(transaction.getLifecycleStateRequest());
+ assertTrue(transactionItem instanceof EnterPipRequestedItem);
}
@Test
public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
doReturn(false).when(activity).inPinnedWindowingMode();
doReturn(false).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(lifecycleManager, atLeast(0))
- .scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
- // Check that none are enter pip request items.
- transaction.getCallbacks().forEach(clientTransactionItem -> {
- assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
- });
+ verify(mClientLifecycleManager, never()).scheduleTransaction(any(), any());
}
@Test
public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
doReturn(true).when(activity).inPinnedWindowingMode();
+ clearInvocations(mClientLifecycleManager);
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- verify(lifecycleManager, atLeast(0))
- .scheduleTransaction(mClientTransactionCaptor.capture());
- final ClientTransaction transaction = mClientTransactionCaptor.getValue();
- // Check that none are enter pip request items.
- transaction.getCallbacks().forEach(clientTransactionItem -> {
- assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
- });
+ verify(mClientLifecycleManager, never()).scheduleTransaction(any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 0d4c443..c6796dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -787,22 +787,44 @@
public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
spyOn(mController);
doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(true);
assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
}
+ @Test
+ public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(false);
+
+ assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
+ }
@Test
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
spyOn(mController);
doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(true);
assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_PORTRAIT));
}
@Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_respectOrientationReqOverUserFullScreenAndSystem() {
+ spyOn(mController);
+ doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
+ mDisplayContent.setIgnoreOrientationRequest(false);
+
+ assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
spyOn(mController);
doReturn(false).when(mController).shouldApplyUserFullscreenOverride();
@@ -872,14 +894,6 @@
}
@Test
- public void testShouldApplyUserFullscreenOverride_disabledIgnoreOrientationRequest() {
- prepareActivityThatShouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(false);
-
- assertFalse(mController.shouldApplyUserFullscreenOverride());
- }
-
- @Test
public void testShouldApplyUserFullscreenOverride_returnsTrue() {
prepareActivityThatShouldApplyUserFullscreenOverride();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 491d5b5..8de45b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -202,8 +202,7 @@
any() /* starting */, anyInt() /* configChanges */,
anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
- ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
- doNothing().when(lifecycleManager).scheduleTransaction(any());
+ doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
startRecentsActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a7c14c3..c3102e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -1301,6 +1302,27 @@
}
@Test
+ public void testShouldCreateCompatDisplayUserAspectRatioFullscreenOverride() {
+ setUpDisplaySizeWithApp(1000, 2500);
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+
+ // Create an activity on the same task.
+ final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
+ RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ // Simulate the user selecting the fullscreen user aspect ratio override
+ spyOn(activity.mWmService.mLetterboxConfiguration);
+ spyOn(activity.mLetterboxUiController);
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(activity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.NEVER_SANDBOX_DISPLAY_APIS})
public void testNeverSandboxDisplayApis_configEnabled_sandboxingNotApplied() {
setUpDisplaySizeWithApp(1000, 1200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 4a33594..6655932 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -31,7 +31,9 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -143,6 +145,15 @@
secureWindow.mAttrs.flags |= FLAG_SECURE;
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
+
+ // Verifies that if the snapshot can be cached, then getSnapshotMode should be respected.
+ // Otherwise a real snapshot can be taken even if the activity disables recents screenshot.
+ spyOn(mWm.mTaskSnapshotController);
+ final int disabledInRecentsTaskId = disabledWindow.getTask().mTaskId;
+ mAtm.takeTaskSnapshot(disabledInRecentsTaskId, true /* updateCache */);
+ verify(mWm.mTaskSnapshotController, never()).prepareTaskSnapshot(any(), any());
+ mAtm.takeTaskSnapshot(disabledInRecentsTaskId, false /* updateCache */);
+ verify(mWm.mTaskSnapshotController).prepareTaskSnapshot(any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index b89182d..46cff8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -306,29 +306,28 @@
@Test
public void testCachedStateConfigurationChange() throws RemoteException {
- final ClientLifecycleManager clientManager = mAtm.getLifecycleManager();
- doNothing().when(clientManager).scheduleTransaction(any(), any());
+ doNothing().when(mClientLifecycleManager).scheduleTransaction(any(), any());
final IApplicationThread thread = mWpc.getThread();
final Configuration newConfig = new Configuration(mWpc.getConfiguration());
newConfig.densityDpi += 100;
// Non-cached state will send the change directly.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
- clearInvocations(clientManager);
+ clearInvocations(mClientLifecycleManager);
mWpc.onConfigurationChanged(newConfig);
- verify(clientManager).scheduleTransaction(eq(thread), any());
+ verify(mClientLifecycleManager).scheduleTransaction(eq(thread), any());
// Cached state won't send the change.
- clearInvocations(clientManager);
+ clearInvocations(mClientLifecycleManager);
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
newConfig.densityDpi += 100;
mWpc.onConfigurationChanged(newConfig);
- verify(clientManager, never()).scheduleTransaction(eq(thread), any());
+ verify(mClientLifecycleManager, never()).scheduleTransaction(eq(thread), any());
// Cached -> non-cached will send the previous deferred config immediately.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
final ArgumentCaptor<ConfigurationChangeItem> captor =
ArgumentCaptor.forClass(ConfigurationChangeItem.class);
- verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
+ verify(mClientLifecycleManager).scheduleTransaction(eq(thread), captor.capture());
final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
captor.getValue().preExecute(client);
final ArgumentCaptor<Configuration> configCaptor =
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e0ed642..df4af11 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -152,6 +152,7 @@
ActivityTaskManagerService mAtm;
RootWindowContainer mRootWindowContainer;
ActivityTaskSupervisor mSupervisor;
+ ClientLifecycleManager mClientLifecycleManager;
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
private Session mTestSession;
@@ -215,6 +216,7 @@
mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
mSupervisor = mAtm.mTaskSupervisor;
mRootWindowContainer = mAtm.mRootWindowContainer;
+ mClientLifecycleManager = mAtm.getLifecycleManager();
mWm = mSystemServicesTestRule.getWindowManagerService();
mOriginalPerDisplayFocusEnabled = mWm.mPerDisplayFocusEnabled;
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f64ab22..63de41f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -206,6 +206,7 @@
static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;
static final int MSG_UID_REMOVED = 10;
static final int MSG_USER_STARTED = 11;
+ static final int MSG_NOTIFY_USAGE_EVENT_LISTENER = 12;
private final Object mLock = new Object();
private Handler mHandler;
@@ -315,6 +316,16 @@
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
return true;
}
+ case MSG_NOTIFY_USAGE_EVENT_LISTENER: {
+ final int userId = msg.arg1;
+ final Event event = (Event) msg.obj;
+ synchronized (mUsageEventListeners) {
+ final int size = mUsageEventListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
+ }
+ }
+ }
}
return false;
};
@@ -532,9 +543,6 @@
}
reportEvent(unlockEvent, userId);
- mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
- userId, 0).sendToTarget();
-
// Remove all the stats stored in system DE.
deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
@@ -546,6 +554,8 @@
userService.persistActiveStats();
}
}
+
+ mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget();
}
/**
@@ -1240,12 +1250,7 @@
service.reportEvent(event);
}
- synchronized (mUsageEventListeners) {
- final int size = mUsageEventListeners.size();
- for (int i = 0; i < size; ++i) {
- mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
- }
- }
+ mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
}
private String getUsageSourcePackage(Event event) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index bb6cf52..fb18375 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -52,9 +52,12 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA;
-import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_EGRESS_LIMIT_REACHED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_REMOTE_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
import static com.android.server.voiceinteraction.HotwordDetectionConnection.ENFORCE_HOTWORD_PHRASE_ID;
import android.annotation.NonNull;
@@ -73,7 +76,9 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
@@ -94,6 +99,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.policy.AppOpsPolicy;
import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener;
@@ -180,6 +186,13 @@
private static final int METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION =
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_STATUS_REPORTED_EXCEPTION;
+ private static final int HOTWORD_EVENT_TYPE_DETECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+ private static final int HOTWORD_EVENT_TYPE_REJECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+ private static final int HOTWORD_EVENT_TYPE_TRAINING_DATA =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
+
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
final ScheduledExecutorService mScheduledExecutorService;
@@ -516,6 +529,7 @@
if (result != null) {
Slog.i(TAG, "Egressed 'hotword rejected result' "
+ "from hotword trusted process");
+ logEgressSizeStats(result);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + result);
}
@@ -608,6 +622,7 @@
Slog.i(TAG, "Egressed "
+ HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG,
"Egressed detected result: " + newResult);
@@ -624,6 +639,32 @@
mVoiceInteractionServiceUid);
}
+ void logEgressSizeStats(HotwordTrainingData data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_TRAINING_DATA);
+ }
+
+ void logEgressSizeStats(HotwordDetectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_DETECTION);
+
+ }
+
+ void logEgressSizeStats(HotwordRejectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_REJECTION);
+ }
+
+ /** Logs event size stats for events egressed from trusted hotword detection service. */
+ private void logEgressSizeStats(Parcelable data, int eventType) {
+ BackgroundThread.getExecutor().execute(() -> {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeValue(data);
+ int dataSizeBytes = parcel.dataSize();
+ parcel.recycle();
+
+ HotwordMetricsLogger.writeHotwordDataEgressSize(eventType, dataSizeBytes,
+ getDetectorType(), mVoiceInteractionServiceUid);
+ });
+ }
+
/** Used to send training data.
*
* @hide
@@ -723,6 +764,7 @@
mVoiceInteractionServiceUid);
throw e;
}
+ logEgressSizeStats(data);
}
void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 6418f3e..2938a58 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -186,6 +186,7 @@
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
+ logEgressSizeStats(newResult);
}
}
@@ -228,6 +229,7 @@
if (mDebugHotwordLogging && result != null) {
Slog.i(TAG, "Egressed rejected result: " + result);
}
+ logEgressSizeStats(result);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
index f7b66a2..ca72c85 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
@@ -34,6 +34,9 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
import android.content.Context;
@@ -120,6 +123,16 @@
}
/**
+ * Logs hotword event egress size metrics.
+ */
+ public static void writeHotwordDataEgressSize(int eventType, long eventSize, int detectorType,
+ int uid) {
+ int metricsDetectorType = getHotwordEventEgressSizeDetectorType(detectorType);
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_EGRESS_SIZE_ATOM_REPORTED,
+ eventType, eventSize, metricsDetectorType, uid);
+ }
+
+ /**
* Starts a {@link LatencyTracker} log for the time it takes to show the
* {@link android.service.voice.VoiceInteractionSession} system UI after a voice trigger.
*
@@ -224,4 +237,15 @@
return AUDIO_EGRESS_NORMAL_DETECTOR;
}
}
+
+ private static int getHotwordEventEgressSizeDetectorType(int detectorType) {
+ switch (detectorType) {
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+ default:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index 2e23eff..9de7f9a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -179,6 +179,7 @@
}
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
@@ -194,6 +195,7 @@
HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED,
mVoiceInteractionServiceUid);
+ logEgressSizeStats(result);
// onRejected isn't allowed here, and we are not expecting it.
}
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
index b1a7d81..8c6e101 100644
--- a/telecomm/java/android/telecom/CallAttributes.java
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -24,6 +24,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -83,6 +85,7 @@
/** @hide */
@IntDef(value = {DIRECTION_INCOMING, DIRECTION_OUTGOING})
+ @Retention(RetentionPolicy.SOURCE)
public @interface Direction {
}
/**
@@ -96,6 +99,7 @@
/** @hide */
@IntDef(value = {AUDIO_CALL, VIDEO_CALL})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallType {
}
/**
@@ -110,6 +114,7 @@
/** @hide */
@IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallCapability {
}
/**
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 3158ad8..287aa65 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -7,10 +7,12 @@
tgunn@google.com
huiwang@google.com
jayachandranc@google.com
-chinmayd@google.com
amruthr@google.com
sasindran@google.com
# Requiring TL ownership for new carrier config keys.
per-file CarrierConfigManager.java=set noparent
per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
+
+#Domain Selection is jointly owned, add additional owners for domain selection specific files
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index baeff06..c124079 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8875,6 +8875,19 @@
KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
/**
+ * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+ * of child session.
+ * Possible values are:
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+ KEY_PREFIX + "child_session_aes_gcm_key_size_int_array";
+
+ /**
* List of supported encryption algorithms for child session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
@@ -8883,6 +8896,16 @@
KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
/**
+ * List of supported AEAD algorithms for child session. Possible values are
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+ KEY_PREFIX + "supported_child_session_aead_algorithms_int_array";
+
+ /**
* Time in seconds after which the IKE session is terminated if rekey procedure is not
* successful. If not set or set to <= 0, default value is 3600 seconds.
*/
@@ -8919,6 +8942,19 @@
KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
/**
+ * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+ * of IKE session.
+ * Possible values -
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+ * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+ KEY_PREFIX + "ike_session_encryption_aes_gcm_key_size_int_array";
+
+ /**
* List of supported encryption algorithms for IKE session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
* {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
@@ -8927,6 +8963,16 @@
KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
/**
+ * List of supported AEAD algorithms for IKE session. Possible values are
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+ public static final String KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+ KEY_PREFIX + "supported_ike_session_aead_algorithms_int_array";
+
+ /**
* List of supported integrity algorithms for IKE session. Possible values are
* {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
* {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
@@ -9156,9 +9202,13 @@
KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
+ KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
defaults.putIntArray(
+ KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
new int[] {
SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
@@ -9207,6 +9257,10 @@
SaProposal.KEY_LEN_AES_192,
SaProposal.KEY_LEN_AES_256});
defaults.putIntArray(
+ KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
+ KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+ defaults.putIntArray(
KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
defaults.putIntArray(
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
index b00c573..71df1f2 100644
--- a/telephony/java/android/telephony/NumberVerificationCallback.java
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -20,6 +20,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A callback for number verification. After a request for number verification is received,
* the system will call {@link #onCallReceived(String)} if a phone call was received from a number
@@ -34,6 +37,7 @@
REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
REASON_IN_EMERGENCY_CALL},
prefix = {"REASON_"})
+ @Retention(RetentionPolicy.SOURCE)
@interface NumberVerificationFailureReason {}
/**
diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java
index b8c1ffe..14713c7 100644
--- a/telephony/java/android/telephony/PinResult.java
+++ b/telephony/java/android/telephony/PinResult.java
@@ -25,6 +25,8 @@
import com.android.internal.telephony.PhoneConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -46,6 +48,7 @@
PIN_RESULT_TYPE_FAILURE,
PIN_RESULT_TYPE_ABORTED,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface PinResultType {}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1c5761d..b96914e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3400,6 +3400,7 @@
SIM_STATE_LOADED,
SIM_STATE_PRESENT,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SimState {}
/**
@@ -10170,6 +10171,7 @@
CALL_COMPOSER_STATUS_ON,
CALL_COMPOSER_STATUS_OFF,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface CallComposerStatus {}
/**
@@ -13157,7 +13159,7 @@
CARRIER_RESTRICTION_STATUS_RESTRICTED,
CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER
})
-
+ @Retention(RetentionPolicy.SOURCE)
public @interface CarrierRestrictionStatus {
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 4b1a726..3e87872 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -951,8 +951,8 @@
* See 3GPP TS 23.501 section 5.6.13
*
* @return True if the PDU session for this APN should always be on and false otherwise
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
public boolean isAlwaysOn() {
return mAlwaysOn;
}
@@ -2282,9 +2282,9 @@
* See 3GPP TS 23.501 section 5.6.13
*
* @param alwaysOn the always on status to set for this APN
- * @hide
*/
- public Builder setAlwaysOn(boolean alwaysOn) {
+ @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
+ public @NonNull Builder setAlwaysOn(boolean alwaysOn) {
this.mAlwaysOn = alwaysOn;
return this;
}
diff --git a/telephony/java/android/telephony/data/ThrottleStatus.java b/telephony/java/android/telephony/data/ThrottleStatus.java
index 0335c68..0dff6ff 100644
--- a/telephony/java/android/telephony/data/ThrottleStatus.java
+++ b/telephony/java/android/telephony/data/ThrottleStatus.java
@@ -27,6 +27,8 @@
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -52,6 +54,7 @@
ThrottleStatus.THROTTLE_TYPE_NONE,
ThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ThrottleType {
}
@@ -76,6 +79,7 @@
ThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
ThrottleStatus.RETRY_TYPE_HANDOVER,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface RetryType {
}
diff --git a/telephony/java/android/telephony/ims/MediaQualityStatus.java b/telephony/java/android/telephony/ims/MediaQualityStatus.java
index 76394fe..e2df0d4 100644
--- a/telephony/java/android/telephony/ims/MediaQualityStatus.java
+++ b/telephony/java/android/telephony/ims/MediaQualityStatus.java
@@ -24,6 +24,8 @@
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.TransportType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -49,6 +51,7 @@
MEDIA_SESSION_TYPE_AUDIO,
MEDIA_SESSION_TYPE_VIDEO,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface MediaSessionType {}
/**
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index f367e40..39c9d8b 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -35,6 +37,7 @@
/**@hide*/
@StringDef(prefix = "RCS_PROFILE_",
value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4})
+ @Retention(RetentionPolicy.SOURCE)
public @interface StringRcsProfile {}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 15a20cb..4c53f8a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3147,4 +3147,16 @@
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void unregisterForSatelliteCapabilitiesChanged(int subId,
in ISatelliteCapabilitiesCallback callback);
+
+ /**
+ * This API can be used by only CTS to override the cached value for the device overlay config
+ * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
+ * outgoing satellite datagrams should be sent to modem in demo mode.
+ *
+ * @param shouldSendToDemoMode Whether send datagram in demo mode should be sent to satellite
+ * modem or not.
+ *
+ * @return {@code true} if the operation is successful, {@code false} otherwise.
+ */
+ boolean setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode);
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index 421ceb7..07b7338 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,7 +50,7 @@
public class VibratorManagerServicePermissionTest {
private static final String PACKAGE_NAME = "com.android.framework.permission.tests";
- private static final int DISPLAY_ID = 1;
+ private static final int DEVICE_ID = 1;
private static final CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -107,7 +107,7 @@
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -117,7 +117,7 @@
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -127,7 +127,7 @@
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -137,7 +137,7 @@
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 159c6fd..c638873 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2514,6 +2514,28 @@
}
}
+ // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
+ // values from.
+ std::vector<std::string> all_feature_flags_args;
+ for (const std::string& arg : feature_flags_args_) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
+ context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ all_feature_flags_args.push_back(arg);
+ }
+ }
+
+ for (const std::string& arg : all_feature_flags_args) {
+ if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
+ return 1;
+ }
+ }
+
if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
&options_.stable_id_map)) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index a08f385..26713fd 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -17,11 +17,17 @@
#ifndef AAPT2_LINK_H
#define AAPT2_LINK_H
+#include <optional>
#include <regex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
#include "Command.h"
#include "Resource.h"
#include "androidfw/IDiagnostics.h"
+#include "cmd/Util.h"
#include "format/binary/TableFlattener.h"
#include "format/proto/ProtoSerialize.h"
#include "link/ManifestFixer.h"
@@ -72,6 +78,7 @@
bool use_sparse_encoding = false;
std::unordered_set<std::string> extensions_to_not_compress;
std::optional<std::regex> regex_to_not_compress;
+ FeatureFlagValues feature_flag_values;
// Static lib options.
bool no_static_lib_packages = false;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index a92f24b..678d846 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -113,6 +113,56 @@
return std::move(filter);
}
+bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values) {
+ if (arg.empty()) {
+ return true;
+ }
+
+ for (StringPiece flag_and_value : util::Tokenize(arg, ',')) {
+ std::vector<std::string> parts = util::Split(flag_and_value, '=');
+ if (parts.empty()) {
+ continue;
+ }
+
+ if (parts.size() > 2) {
+ diag->Error(android::DiagMessage()
+ << "Invalid feature flag and optional value '" << flag_and_value
+ << "'. Must be in the format 'flag_name[=true|false]");
+ return false;
+ }
+
+ StringPiece flag_name = util::TrimWhitespace(parts[0]);
+ if (flag_name.empty()) {
+ diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
+ return false;
+ }
+
+ std::optional<bool> flag_value = {};
+ if (parts.size() == 2) {
+ StringPiece str_flag_value = util::TrimWhitespace(parts[1]);
+ if (!str_flag_value.empty()) {
+ flag_value = ResourceUtils::ParseBool(parts[1]);
+ if (!flag_value.has_value()) {
+ diag->Error(android::DiagMessage() << "Invalid value for feature flag '" << flag_and_value
+ << "'. Value must be 'true' or 'false'");
+ return false;
+ }
+ }
+ }
+
+ if (auto [it, inserted] =
+ out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
+ !inserted) {
+ // We are allowing the same flag to appear multiple times, last value wins.
+ diag->Note(android::DiagMessage()
+ << "Value for feature flag '" << flag_name << "' was given more than once");
+ it->second = flag_value;
+ }
+ }
+ return true;
+}
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the minSdk. Otherwise the resources that have had
// their SDK version stripped due to minSdk won't ever match.
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 712c07b..9ece5dd 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -17,8 +17,13 @@
#ifndef AAPT_SPLIT_UTIL_H
#define AAPT_SPLIT_UTIL_H
+#include <functional>
+#include <map>
+#include <memory>
+#include <optional>
#include <regex>
#include <set>
+#include <string>
#include <unordered_set>
#include "AppInfo.h"
@@ -32,6 +37,8 @@
namespace aapt {
+using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>;
+
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
std::optional<uint16_t> ParseTargetDensityParameter(android::StringPiece arg,
@@ -48,6 +55,13 @@
std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args,
android::IDiagnostics* diag);
+// Parses a feature flags parameter, which can contain one or more pairs of flag names and optional
+// values, and fills in `out_feature_flag_values` with the parsed values. The pairs in the argument
+// are separated by ',' and the name is separated from the value by '=' if there is a value given.
+// Example arg: "flag1=true,flag2=false,flag3=,flag4" where flag3 and flag4 have no given value.
+bool ParseFeatureFlagsParameter(android::StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values);
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the min_sdk. Otherwise the resources that have had
// their SDK version stripped due to min_sdk won't ever match.
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 139bfbc..723d87e 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -25,6 +25,7 @@
#include "util/Files.h"
using ::android::ConfigDescription;
+using testing::Pair;
using testing::UnorderedElementsAre;
namespace aapt {
@@ -354,6 +355,51 @@
EXPECT_CONFIG_EQ(constraints, expected_configuration);
}
+TEST(UtilTest, ParseFeatureFlagsParameter_Empty) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("", diagnostics, &feature_flag_values));
+ EXPECT_TRUE(feature_flag_values.empty());
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_TooManyParts) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=bar=baz", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_NoNameGiven) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,=false", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,bar=42", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(
+ ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values));
+ EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)),
+ Pair("bar", std::optional<bool>(true))));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_Valid) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics,
+ &feature_flag_values));
+ EXPECT_THAT(feature_flag_values,
+ UnorderedElementsAre(Pair("foo", std::optional<bool>(true)),
+ Pair("bar", std::optional<bool>(false)),
+ Pair("baz", std::nullopt), Pair("quux", std::nullopt)));
+}
+
TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 25abbac..c770b9c 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -65,7 +65,7 @@
*/
public static void onThrowMethodCalled() {
// TODO: Maybe add call tracking?
- throw new AssumptionViolatedException("This method is not supported on the host side");
+ throw new RuntimeException("This method is not supported on the host side");
}
/**
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index b133c2a..248121c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -377,7 +377,7 @@
throw HostStubGenInternalException("Policy $policy shouldn't show up here")
}
- val suffix = getAnnotationField(an, "suffix") ?: return@let
+ val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
val renameFrom = mn.name + suffix
val renameTo = mn.name
@@ -387,13 +387,17 @@
}
// This mn has "SubstituteWith". This means,
- // 1. Re move the "rename-to" method, so add it to substitutedMethods.
+ // 1. Re move the "rename-to" method, so add it to substitutedMethods.
policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
FilterPolicy.Remove.withReason("substitute-to")
+ // If the policy is "stub", use "stub".
+ // Otherwise, it must be "keep" or "throw", but there's no point in using
+ // "throw", so let's use "keep".
+ val newPolicy = if (policy.needsInStub) policy else FilterPolicy.Keep
// 2. We also keep the from-to in the map.
policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
- policy.withReason("substitute-from")
+ newPolicy.withReason("substitute-from")
substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
@@ -405,10 +409,11 @@
/**
* Return the (String) value of 'value' parameter from an annotation.
*/
- private fun getAnnotationField(an: AnnotationNode, name: String): String? {
+ private fun getAnnotationField(an: AnnotationNode, name: String,
+ required: Boolean = true): String? {
try {
val suffix = findAnnotationValueAsString(an, name)
- if (suffix == null) {
+ if (suffix == null && required) {
errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
}
return suffix
@@ -438,4 +443,4 @@
return ret
}
}
-}
\ No newline at end of file
+}
diff --git a/tools/lint/README.md b/tools/lint/README.md
index b235ad6..ff8e442 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -103,10 +103,15 @@
As noted above, this baseline file contains warnings too, which might be undesirable. For example,
CI tools might surface these warnings in code reviews. In order to create this file without
-warnings, we need to pass another flag to lint: `--nowarn`. The easiest way to do this is to
-locally change the soong code in
-[lint.go](http://cs/aosp-master/build/soong/java/lint.go;l=451;rcl=2e778d5bc4a8d1d77b4f4a3029a4a254ad57db75)
-adding `cmd.Flag("--nowarn")` and running lint again.
+warnings, we need to pass another flag to lint: `--nowarn`. One option is to add the flag to your
+Android.bp file and then run lint again:
+
+```
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ flags: ["--nowarn"],
+ }
+```
# Documentation
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index d41fee3..24d203f 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -24,33 +24,31 @@
import org.jetbrains.uast.UMethod
/**
- * Given a UMethod, determine if this method is
- * the entrypoint to an interface generated by AIDL,
- * returning the interface name if so, otherwise returning null
+ * Given a UMethod, determine if this method is the entrypoint to an interface
+ * generated by AIDL, returning the interface name if so, otherwise returning
+ * null
*/
fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? {
- if (!isContainedInSubclassOfStub(context, node)) return null
- for (superMethod in node.findSuperMethods()) {
- for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
- ?: continue) {
- if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
- return superMethod.containingClass?.name
- }
- }
+ val containingStub = containingStub(context, node) ?: return null
+ val superMethod = node.findSuperMethods(containingStub)
+ if (superMethod.isEmpty()) return null
+ return containingStub.containingClass?.name
+}
+
+/* Returns the containing Stub class if any. This is not sufficient to infer
+ * that the method itself extends an AIDL generated method. See
+ * getContainingAidlInterface for that purpose.
+ */
+fun containingStub(context: JavaContext, node: UMethod?): PsiClass? {
+ var superClass = node?.containingClass?.superClass
+ while (superClass != null) {
+ if (isStub(context, superClass)) return superClass
+ superClass = superClass.superClass
}
return null
}
-fun isContainedInSubclassOfStub(context: JavaContext, node: UMethod?): Boolean {
- var superClass = node?.containingClass?.superClass
- while (superClass != null) {
- if (isStub(context, superClass)) return true
- superClass = superClass.superClass
- }
- return false
-}
-
-fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
+private fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
if (psiClass == null) return false
if (psiClass.name != "Stub") return false
if (!context.evaluator.isStatic(psiClass)) return false
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 935bade..624a198 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -20,6 +20,7 @@
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.parcel.SaferParcelChecker
+import com.google.android.lint.aidl.PermissionAnnotationDetector
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@@ -37,6 +38,7 @@
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
// TODO: Currently crashes due to OOM issue
// PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
new file mode 100644
index 0000000..6b50cfd
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+/**
+ * Ensures all AIDL-generated methods are annotated.
+ *
+ * This detector is run on system_server to validate that any method that may
+ * be exposed via an AIDL interface is permission-annotated. That is, it must
+ * have one of the following annotation:
+ * - @EnforcePermission
+ * - @RequiresNoPermission
+ * - @PermissionManuallyEnforced
+ */
+class PermissionAnnotationDetector : AidlImplementationDetector() {
+
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ if (context.evaluator.isAbstract(node)) return
+
+ if (AIDL_PERMISSION_ANNOTATIONS.any { node.hasAnnotation(it) }) return
+
+ context.report(
+ ISSUE_MISSING_PERMISSION_ANNOTATION,
+ node,
+ context.getLocation(node),
+ "The method ${node.name} is not permission-annotated."
+ )
+ }
+
+ companion object {
+
+ private val EXPLANATION_MISSING_PERMISSION_ANNOTATION = """
+ Interfaces that are exposed by system_server are required to have an annotation which
+ denotes the type of permission enforced. There are 3 possible options:
+ - @EnforcePermission
+ - @RequiresNoPermission
+ - @PermissionManuallyEnforced
+ See the documentation of each annotation for further details.
+
+ The annotation on the Java implementation must be the same that the AIDL interface
+ definition. This is verified by a lint in the build system.
+ """.trimIndent()
+
+ @JvmField
+ val ISSUE_MISSING_PERMISSION_ANNOTATION = Issue.create(
+ id = "MissingPermissionAnnotation",
+ briefDescription = "No permission annotation on exposed AIDL interface.",
+ explanation = EXPLANATION_MISSING_PERMISSION_ANNOTATION,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ PermissionAnnotationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ enabledByDefault = false
+ )
+ }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
new file mode 100644
index 0000000..bce848a
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class PermissionAnnotationDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = PermissionAnnotationDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission("android.Manifest.permission.READ_CONTACTS")
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMissingAnnotation() {
+ lint().files(
+ java(
+ """
+ public class Bar extends IBar.Stub {
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Bar.java:2: Error: The method testMethod is not permission-annotated. [MissingPermissionAnnotation]
+ public void testMethod() { }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testNoIssueWhenExtendingWithAnotherSubclass() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() { }
+ // not an AIDL method, just another method
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ java(
+ """
+ public class Baz extends Bar {
+ @Override
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /* Stubs */
+
+ // A service with permission annotation on the method.
+ private val interfaceIFoo: TestFile = java(
+ """
+ public interface IFoo extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFoo {
+ }
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ @Override
+ @android.annotation.RequiresNoPermission
+ public void testMethodNoPermission();
+ @Override
+ @android.annotation.PermissionManuallyEnforced
+ public void testMethodManual();
+ }
+ """
+ ).indented()
+
+ // A service with no permission annotation.
+ private val interfaceIBar: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFoo, interfaceIBar)
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 83b8f16..4455a9c 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -168,7 +168,7 @@
annotationInfo.origin == AnnotationOrigin.METHOD) {
/* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
val uMethod = element as? UMethod ?: return
- if (!isContainedInSubclassOfStub(context, uMethod)) {
+ if (getContainingAidlInterface(context, uMethod) == null) {
return
}
val overridingMethod = element.sourcePsi as PsiMethod
@@ -184,7 +184,8 @@
if (context.evaluator.isAbstract(node)) return
if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
- if (!isContainedInSubclassOfStub(context, node)) {
+ val stubClass = containingStub(context, node)
+ if (stubClass == null) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
node,
@@ -196,7 +197,7 @@
/* Check that we are connected to the super class */
val overridingMethod = node as PsiMethod
- val parents = overridingMethod.findSuperMethods()
+ val parents = overridingMethod.findSuperMethods(stubClass)
if (parents.isEmpty()) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index d8afcb9..2afca05 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -176,6 +176,29 @@
""".addLineContinuation())
}
+ fun testDetectNoIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass43 extends IFooMethod.Stub {
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(), java(
+ """
+ package test.pkg;
+ public class TestClass44 extends TestClass43 {
+ @Override
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
fun testDetectIssuesEmptyAnnotationOnMethod() {
lint().files(java(
"""
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index ebda6f1..4f5e0e4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -70,14 +70,8 @@
private List<HotspotNetwork> mHotspotNetworks = Collections.emptyList();
private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
private SharedConnectivitySettingsState mSettingsState = null;
- private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus =
- new HotspotNetworkConnectionStatus.Builder()
- .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
- .setExtras(Bundle.EMPTY).build();
- private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus =
- new KnownNetworkConnectionStatus.Builder()
- .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
- .setExtras(Bundle.EMPTY).build();
+ private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus = null;
+ private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus = null;
// Used for testing
private CountDownLatch mCountDownLatch;
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index c6f6798..48ac82d 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -394,6 +394,26 @@
verify(mCallback, never()).onKnownNetworkConnectionStatusChanged(any());
}
+ @Test
+ public void getHotspotNetworkConnectionStatus_withoutUpdate_returnsNull()
+ throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ assertThat(binder.getHotspotNetworkConnectionStatus()).isNull();
+ }
+
+ @Test
+ public void getKnownNetworkConnectionStatus_withoutUpdate_returnsNull()
+ throws RemoteException {
+ SharedConnectivityService service = createService();
+ ISharedConnectivityService.Stub binder =
+ (ISharedConnectivityService.Stub) service.onBind(new Intent());
+
+ assertThat(binder.getKnownNetworkConnectionStatus()).isNull();
+ }
+
private FakeSharedConnectivityService createService() {
FakeSharedConnectivityService service = new FakeSharedConnectivityService();
service.attachBaseContext(mContext);