Merge "Remove box shaped ripple flag." into main
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index bcfb68f..6b8c02f 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -183,6 +183,7 @@
"-federationapi AndroidX $(location :current-androidx-api)",
// doclava contains checks for a few issues that are have been migrated to metalava.
// disable them in doclava, to avoid mistriggering or double triggering.
+ "-hide 101", // TODO: turn Lint 101 back into an error again
"-hide 111", // HIDDEN_SUPERCLASS
"-hide 113", // DEPRECATION_MISMATCH
"-hide 125", // REQUIRES_PERMISSION
diff --git a/api/api.go b/api/api.go
index 2668999..43713aa 100644
--- a/api/api.go
+++ b/api/api.go
@@ -64,6 +64,7 @@
type CombinedApis struct {
android.ModuleBase
+ android.DefaultableModuleBase
properties CombinedApisProperties
}
@@ -74,6 +75,7 @@
func registerBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+ ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory)
}
var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
@@ -409,6 +411,7 @@
module := &CombinedApis{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
+ android.InitDefaultableModule(module)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
return module
}
@@ -445,3 +448,16 @@
}
return s2
}
+
+// Defaults
+type CombinedApisModuleDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func CombinedApisModuleDefaultsFactory() android.Module {
+ module := &CombinedApisModuleDefaults{}
+ module.AddProperties(&CombinedApisProperties{})
+ android.InitDefaultsModule(module)
+ return module
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 4156a78..7c34812 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -49,6 +49,7 @@
field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE";
field public static final String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
field public static final String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
+ field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String BIND_TV_AD_SERVICE = "android.permission.BIND_TV_AD_SERVICE";
field public static final String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
field public static final String BIND_TV_INTERACTIVE_APP = "android.permission.BIND_TV_INTERACTIVE_APP";
field public static final String BIND_VISUAL_VOICEMAIL_SERVICE = "android.permission.BIND_VISUAL_VOICEMAIL_SERVICE";
@@ -19733,7 +19734,7 @@
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class LensIntrinsicsSample {
ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public LensIntrinsicsSample(long, @NonNull float[]);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public float[] getLensIntrinsics();
- method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestamp();
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestampNanos();
}
public final class LensShadingMap {
@@ -33400,7 +33401,7 @@
@FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings {
method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor);
- method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor);
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestampMillis(@NonNull android.os.PowerMonitor);
field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff
}
@@ -33995,8 +33996,8 @@
}
public class SystemHealthManager {
- method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>);
- method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>);
+ method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
method public android.os.health.HealthStats takeMyUidSnapshot();
method public android.os.health.HealthStats takeUidSnapshot(int);
method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
@@ -47294,6 +47295,7 @@
method public int getLineForOffset(int);
method public int getLineForVertical(int);
method public float getLineLeft(int);
+ method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean);
method public float getLineMax(int);
method public float getLineRight(int);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
@@ -56499,6 +56501,7 @@
field public static final String TYPE_EMAIL = "email";
field public static final String TYPE_FLIGHT_NUMBER = "flight";
field public static final String TYPE_OTHER = "other";
+ field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String TYPE_OTP_CODE = "otp_code";
field public static final String TYPE_PHONE = "phone";
field public static final String TYPE_UNKNOWN = "";
field public static final String TYPE_URL = "url";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2898705..c9e12c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -862,6 +862,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
}
+ @FlaggedApi("android.app.bic_client") public final class BackgroundInstallControlManager {
+ method @FlaggedApi("android.app.bic_client") @NonNull @RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES) public java.util.List<android.content.pm.PackageInfo> getBackgroundInstalledPackages(long);
+ }
+
public class BroadcastOptions {
method public void clearRequireCompatChange();
method public int getPendingIntentBackgroundActivityStartMode();
@@ -1596,12 +1600,14 @@
method public int getDensityLevel();
method @NonNull public java.time.Instant getEndTime();
method public int getEventType();
+ method @FlaggedApi("android.app.ambient_heart_rate") @IntRange(from=0xffffffff) public int getRatePerMinute();
method @NonNull public java.time.Instant getStartTime();
method @NonNull public android.os.PersistableBundle getVendorData();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
field public static final int EVENT_COUGH = 1; // 0x1
+ field @FlaggedApi("android.app.ambient_heart_rate") public static final int EVENT_HEART_RATE = 4; // 0x4
field public static final int EVENT_SNORE = 2; // 0x2
field public static final int EVENT_UNKNOWN = 0; // 0x0
field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0
@@ -1621,6 +1627,7 @@
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
+ method @FlaggedApi("android.app.ambient_heart_rate") @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setRatePerMinute(@IntRange(from=0xffffffff) int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle);
}
@@ -14681,6 +14688,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
+ method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNullCipherNotificationsEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -14720,6 +14728,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableNullCipherNotifications(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
@@ -15127,7 +15136,7 @@
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16334,7 +16343,7 @@
public interface RegistrationManager {
field public static final int SUGGESTED_ACTION_NONE = 0; // 0x0
- field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4; // 0x4
field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1; // 0x1
field public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2; // 0x2
field @FlaggedApi("com.android.internal.telephony.flags.add_rat_related_suggested_action_to_ims_registration") public static final int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3; // 0x3
@@ -17032,8 +17041,8 @@
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees();
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteElevationDegrees();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffff4c, to=180) public float getSatelliteAzimuthDegrees();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @FloatRange(from=0xffffffa6, to=90) public float getSatelliteElevationDegrees();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
}
@@ -17072,7 +17081,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException;
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -17093,7 +17102,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1
@@ -17163,12 +17172,12 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode();
}
- @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
}
- @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteStateCallback {
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
}
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index b2a28b2..0505af4 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -2359,8 +2359,8 @@
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.provisionSatelliteService(String,byte[],android.os.CancellationSignal,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteDatagram(java.util.concurrent.Executor, android.telephony.satellite.SatelliteDatagramCallback):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteDatagram(java.util.concurrent.Executor,android.telephony.satellite.SatelliteDatagramCallback)
-UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteStateCallback):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteStateCallback)
+UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteModemStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteModemStateCallback):
+ New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteModemStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteModemStateCallback)
UnflaggedApi: android.telephony.satellite.SatelliteManager#registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor, android.telephony.satellite.SatelliteProvisionStateCallback):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.registerForSatelliteProvisionStateChanged(java.util.concurrent.Executor,android.telephony.satellite.SatelliteProvisionStateCallback)
UnflaggedApi: android.telephony.satellite.SatelliteManager#requestIsDemoModeEnabled(java.util.concurrent.Executor, android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>):
@@ -2389,8 +2389,8 @@
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback,java.util.concurrent.Executor,java.util.function.Consumer<java.lang.Integer>)
UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteDatagram(android.telephony.satellite.SatelliteDatagramCallback)
-UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteStateCallback)
+UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback):
+ New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteModemStateChanged(android.telephony.satellite.SatelliteModemStateCallback)
UnflaggedApi: android.telephony.satellite.SatelliteManager#unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.unregisterForSatelliteProvisionStateChanged(android.telephony.satellite.SatelliteProvisionStateCallback)
UnflaggedApi: android.telephony.satellite.SatelliteManager.SatelliteException:
@@ -2403,10 +2403,10 @@
New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteProvisionStateCallback
UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean):
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteProvisionStateCallback.onSatelliteProvisionStateChanged(boolean)
-UnflaggedApi: android.telephony.satellite.SatelliteStateCallback:
- New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteStateCallback
-UnflaggedApi: android.telephony.satellite.SatelliteStateCallback#onSatelliteModemStateChanged(int):
- New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteStateCallback.onSatelliteModemStateChanged(int)
+UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback:
+ New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteModemStateCallback
+UnflaggedApi: android.telephony.satellite.SatelliteModemStateCallback#onSatelliteModemStateChanged(int):
+ New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteModemStateCallback.onSatelliteModemStateChanged(int)
UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback:
New API must be flagged with @FlaggedApi: class android.telephony.satellite.SatelliteTransmissionUpdateCallback
UnflaggedApi: android.telephony.satellite.SatelliteTransmissionUpdateCallback#onReceiveDatagramStateChanged(int, int, int):
diff --git a/core/java/android/app/BackgroundInstallControlManager.java b/core/java/android/app/BackgroundInstallControlManager.java
new file mode 100644
index 0000000..f5b6878
--- /dev/null
+++ b/core/java/android/app/BackgroundInstallControlManager.java
@@ -0,0 +1,101 @@
+/*
+ * 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.app;
+
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.IBackgroundInstallControlService;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.ServiceManager;
+
+import java.util.List;
+
+/**
+ * BackgroundInstallControlManager client allows apps to query apps installed in background.
+ *
+ * <p>Any applications that was installed without an accompanying installer UI activity paired
+ * with recorded user interaction event is considered background installed. This is determined by
+ * analysis of user-activity logs.
+ *
+ * <p>Warning: BackgroundInstallControl should not be considered a reliable or accurate
+ * determination of background install application. Consumers can use this as a supplementary
+ * signal, but must perform additional due diligence to confirm the install nature of the package.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_BIC_CLIENT)
+@SystemApi(client = PRIVILEGED_APPS)
+@SystemService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)
+public final class BackgroundInstallControlManager {
+
+ private static final String TAG = "BackgroundInstallControlManager";
+ private static IBackgroundInstallControlService sService;
+ private final Context mContext;
+
+ BackgroundInstallControlManager(Context context) {
+ mContext = context;
+ }
+
+ private static IBackgroundInstallControlService getService() {
+ if (sService == null) {
+ sService =
+ IBackgroundInstallControlService.Stub.asInterface(
+ ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
+ }
+ return sService;
+ }
+
+ /**
+ * Returns a full list of {@link PackageInfo} of apps currently installed that are considered
+ * installed in the background.
+ *
+ * <p>Refer to top level doc {@link BackgroundInstallControlManager} for more details on
+ * background-installed applications.
+ * <p>
+ *
+ * @param flags - Flags will be used to call
+ * {@link PackageManager#getInstalledPackages(PackageInfoFlags)} to retrieve installed packages.
+ * @return A list of packages retrieved from {@link PackageManager} with non-background
+ * installed app filter applied.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_BIC_CLIENT)
+ @SystemApi
+ @RequiresPermission(QUERY_ALL_PACKAGES)
+ public @NonNull List<PackageInfo> getBackgroundInstalledPackages(
+ @PackageManager.PackageInfoFlagsBits long flags) {
+ try {
+ return getService()
+ .getBackgroundInstalledPackages(flags, mContext.getUserId())
+ .getList();
+ } catch (SecurityException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9cf732a..390fa22 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1603,6 +1603,20 @@
}
});
+ // DO NOT do a flag check like this unless the flag is read-only.
+ // (because this code is executed during preload in zygote.)
+ // If the flag is mutable, the check should be inside CachedServiceFetcher.
+ if (Flags.bicClient()) {
+ registerService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE,
+ BackgroundInstallControlManager.class,
+ new CachedServiceFetcher<BackgroundInstallControlManager>() {
+ @Override
+ public BackgroundInstallControlManager createService(ContextImpl ctx) {
+ return new BackgroundInstallControlManager(ctx);
+ }
+ });
+ }
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/ambient_context.aconfig b/core/java/android/app/ambient_context.aconfig
new file mode 100644
index 0000000..3f73da2
--- /dev/null
+++ b/core/java/android/app/ambient_context.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "biometrics_integration"
+ name: "ambient_heart_rate"
+ description: "Feature flag for adding heart rate api to ambient context."
+ bug: "318309481"
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index b5c66ff..f94987e 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -16,7 +16,9 @@
package android.app.ambientcontext;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
@@ -68,6 +70,14 @@
public static final int EVENT_BACK_DOUBLE_TAP = 3;
/**
+ * The integer indicating a heart rate measurement was done.
+ *
+ * @see #getRatePerMinute
+ */
+ @Event @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+ public static final int EVENT_HEART_RATE = 4;
+
+ /**
* Integer indicating the start of wearable vendor defined events that can be detected.
* These depend on the vendor implementation.
*/
@@ -79,12 +89,16 @@
*/
public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
+ /** Default value for the rate per minute data field. */
+ private static final int RATE_PER_MINUTE_UNKNOWN = -1;
+
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
+ EVENT_HEART_RATE,
EVENT_VENDOR_WEARABLE_START,
})
@Retention(RetentionPolicy.SOURCE)
@@ -170,6 +184,16 @@
return new PersistableBundle();
}
+ /**
+ * Rate per minute of the event during the start to end time.
+ *
+ * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
+ */
+ private final @IntRange(from = -1) int mRatePerMinute;
+ private static int defaultRatePerMinute() {
+ return RATE_PER_MINUTE_UNKNOWN;
+ }
+
// Code below generated by codegen v1.0.23.
@@ -179,6 +203,8 @@
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
+ // then manually add @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE) back to flagged
+ // APIs.
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -191,6 +217,7 @@
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
+ EVENT_HEART_RATE,
EVENT_VENDOR_WEARABLE_START
})
@Retention(RetentionPolicy.SOURCE)
@@ -209,6 +236,8 @@
return "EVENT_SNORE";
case EVENT_BACK_DOUBLE_TAP:
return "EVENT_BACK_DOUBLE_TAP";
+ case EVENT_HEART_RATE:
+ return "EVENT_HEART_RATE";
case EVENT_VENDOR_WEARABLE_START:
return "EVENT_VENDOR_WEARABLE_START";
default: return Integer.toHexString(value);
@@ -255,7 +284,8 @@
@NonNull Instant endTime,
@LevelValue int confidenceLevel,
@LevelValue int densityLevel,
- @NonNull PersistableBundle vendorData) {
+ @NonNull PersistableBundle vendorData,
+ @IntRange(from = -1) int ratePerMinute) {
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
EventCode.class, null, mEventType);
@@ -274,6 +304,10 @@
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
+ this.mRatePerMinute = ratePerMinute;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mRatePerMinute,
+ "from", -1);
// onConstructed(); // You can define this method to get a callback
}
@@ -330,6 +364,17 @@
return mVendorData;
}
+ /**
+ * Rate per minute of the event during the start to end time.
+ *
+ * @return the rate per minute, or {@link #RATE_PER_MINUTE_UNKNOWN} if the rate is unknown.
+ */
+ @DataClass.Generated.Member
+ @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+ public @IntRange(from = -1) int getRatePerMinute() {
+ return mRatePerMinute;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -342,7 +387,8 @@
"endTime = " + mEndTime + ", " +
"confidenceLevel = " + mConfidenceLevel + ", " +
"densityLevel = " + mDensityLevel + ", " +
- "vendorData = " + mVendorData +
+ "vendorData = " + mVendorData + ", " +
+ "ratePerMinute = " + mRatePerMinute +
" }";
}
@@ -380,6 +426,7 @@
dest.writeInt(mConfidenceLevel);
dest.writeInt(mDensityLevel);
dest.writeTypedObject(mVendorData, flags);
+ dest.writeInt(mRatePerMinute);
}
@Override
@@ -399,6 +446,7 @@
int confidenceLevel = in.readInt();
int densityLevel = in.readInt();
PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+ int ratePerMinute = in.readInt();
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
@@ -418,6 +466,10 @@
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
+ this.mRatePerMinute = ratePerMinute;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mRatePerMinute,
+ "from", -1);
// onConstructed(); // You can define this method to get a callback
}
@@ -449,6 +501,7 @@
private @LevelValue int mConfidenceLevel;
private @LevelValue int mDensityLevel;
private @NonNull PersistableBundle mVendorData;
+ private @IntRange(from = -1) int mRatePerMinute;
private long mBuilderFieldsSet = 0L;
@@ -525,10 +578,22 @@
return this;
}
+ /**
+ * Rate per minute of the event during the start to end time.
+ */
+ @DataClass.Generated.Member
+ @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+ public @NonNull Builder setRatePerMinute(@IntRange(from = -1) int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mRatePerMinute = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AmbientContextEvent build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x40; // Mark builder used
+ mBuilderFieldsSet |= 0x80; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mEventType = defaultEventType();
@@ -548,18 +613,22 @@
if ((mBuilderFieldsSet & 0x20) == 0) {
mVendorData = defaultVendorData();
}
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mRatePerMinute = defaultRatePerMinute();
+ }
AmbientContextEvent o = new AmbientContextEvent(
mEventType,
mStartTime,
mEndTime,
mConfidenceLevel,
mDensityLevel,
- mVendorData);
+ mVendorData,
+ mRatePerMinute);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x40) != 0) {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -567,10 +636,10 @@
}
@DataClass.Generated(
- time = 1671217108067L,
+ time = 1704895515931L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
- inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\nprivate static final int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig
new file mode 100644
index 0000000..029b93a
--- /dev/null
+++ b/core/java/android/app/background_install_control_manager.aconfig
@@ -0,0 +1,9 @@
+package: "android.app"
+
+flag {
+ namespace: "background_install_control"
+ name: "bic_client"
+ description: "System API for background install control."
+ is_fixed_read_only: true
+ bug: "287507984"
+}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 6371871..a41cb1f 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -239,7 +239,7 @@
Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e);
}
- byte[] buffer = new byte[32 * 1024];
+ byte[] buffer = new byte[64 * 1024];
final long origSize = size;
FileInputStream in = new FileInputStream(data.getFileDescriptor());
while (size > 0) {
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 90c3d04..a37408b 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -4,6 +4,7 @@
per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
+per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS
per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
per-file Intent.java = file:/INTENT_OWNERS
per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
index c8e7cae..4bc8fe1 100644
--- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl
+++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
@@ -16,11 +16,20 @@
package android.content.pm;
+
import android.content.pm.ParceledListSlice;
+import android.os.IRemoteCallback;
/**
* {@hide}
*/
interface IBackgroundInstallControlService {
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)")
ParceledListSlice getBackgroundInstalledPackages(long flags, int userId);
-}
+
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
+ void registerBackgroundInstallCallback(IRemoteCallback callback);
+
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
+ void unregisterBackgroundInstallCallback(IRemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 3affb73..0cd1c8c 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -79,6 +79,8 @@
import android.util.Range;
import android.util.Size;
+import com.android.internal.camera.flags.Flags;
+
import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMRuntime;
@@ -1795,49 +1797,57 @@
return false;
}
- long[] tsArray = new long[samples.length];
- float[] intrinsicsArray = new float[samples.length * 5];
- for (int i = 0; i < samples.length; i++) {
- tsArray[i] = samples[i].getTimestamp();
- System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5*i, 5);
+ if (Flags.concertMode()) {
+ long[] tsArray = new long[samples.length];
+ float[] intrinsicsArray = new float[samples.length * 5];
+ for (int i = 0; i < samples.length; i++) {
+ tsArray[i] = samples[i].getTimestampNanos();
+ System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5);
+ }
+ setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
+ setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
+
+ return true;
+ } else {
+ return false;
}
- setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
- setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
-
- return true;
}
private LensIntrinsicsSample[] getLensIntrinsicSamples() {
- long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
- float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
+ if (Flags.concertMode()) {
+ long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
+ float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
- if (timestamps == null) {
- if (intrinsics != null) {
- throw new AssertionError("timestamps is null but intrinsics is not");
+ if (timestamps == null) {
+ if (intrinsics != null) {
+ throw new AssertionError("timestamps is null but intrinsics is not");
+ }
+
+ return null;
}
+ if (intrinsics == null) {
+ throw new AssertionError("timestamps is not null but intrinsics is");
+ } else if ((intrinsics.length % 5) != 0) {
+ throw new AssertionError("intrinsics are not multiple of 5");
+ }
+
+ if ((intrinsics.length / 5) != timestamps.length) {
+ throw new AssertionError(String.format(
+ "timestamps has %d entries but intrinsics has %d", timestamps.length,
+ intrinsics.length / 5));
+ }
+
+ LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
+ for (int i = 0; i < timestamps.length; i++) {
+ float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5);
+ samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
+ }
+ return samples;
+ } else {
return null;
}
-
- if (intrinsics == null) {
- throw new AssertionError("timestamps is not null but intrinsics is");
- } else if((intrinsics.length % 5) != 0) {
- throw new AssertionError("intrinsics are not multiple of 5");
- }
-
- if ((intrinsics.length / 5) != timestamps.length) {
- throw new AssertionError(String.format(
- "timestamps has %d entries but intrinsics has %d", timestamps.length,
- intrinsics.length / 5));
- }
-
- LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
- for (int i = 0; i < timestamps.length; i++) {
- float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5*i, 5*i + 5);
- samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
- }
- return samples;
}
private Capability[] getExtendedSceneModeCapabilities() {
diff --git a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
index 575cbfa..9a4ec5c 100644
--- a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
+++ b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
@@ -37,16 +37,18 @@
* Create a new {@link LensIntrinsicsSample}.
*
* <p>{@link LensIntrinsicsSample} contains the timestamp and the
- * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.
+ * {@link CaptureResult#LENS_INTRINSIC_CALIBRATION} sample.</p>
*
- * @param timestamp timestamp of the lens intrinsics sample.
- * @param lensIntrinsics the lens intrinsic calibration for the sample.
+ * @param timestampNs timestamp in nanoseconds of the lens intrinsics sample. This uses the
+ * same time basis as {@link CaptureResult#SENSOR_TIMESTAMP}.
+ * @param lensIntrinsics the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsic}
+ * calibration for the sample.
*
* @throws IllegalArgumentException if lensIntrinsics length is different from 5
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
- public LensIntrinsicsSample(final long timestamp, @NonNull final float[] lensIntrinsics) {
- mTimestampNs = timestamp;
+ public LensIntrinsicsSample(final long timestampNs, @NonNull final float[] lensIntrinsics) {
+ mTimestampNs = timestampNs;
Preconditions.checkArgument(lensIntrinsics.length == 5);
mLensIntrinsics = lensIntrinsics;
}
@@ -54,18 +56,18 @@
/**
* Get the timestamp in nanoseconds.
*
- *<p>The timestamps are in the same timebase as and comparable to
+ *<p>The timestamps are in the same time basis as and comparable to
*{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
*
* @return a long value (guaranteed to be finite)
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
- public long getTimestamp() {
+ public long getTimestampNanos() {
return mTimestampNs;
}
/**
- * Get the lens intrinsics calibration
+ * Get the lens {@link CaptureResult#LENS_INTRINSIC_CALIBRATION intrinsics} calibration
*
* @return a floating point value (guaranteed to be finite)
* @see CaptureResult#LENS_INTRINSIC_CALIBRATION
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 70cf973..c727a60 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -115,6 +115,20 @@
"vcn_restricted_transports";
/**
+ * Key for number of seconds to wait before entering safe mode
+ *
+ * <p>A VcnGatewayConnection will enter safe mode when it takes over the configured timeout to
+ * enter {@link ConnectedState}.
+ *
+ * <p>Defaults to 30, unless overridden by carrier config
+ *
+ * @hide
+ */
+ @NonNull
+ public static final String VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY =
+ "vcn_safe_mode_timeout_seconds_key";
+
+ /**
* Key for maximum number of parallel SAs for tunnel aggregation
*
* <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be
@@ -135,6 +149,7 @@
VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY,
VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
};
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index 6956916..67a1906 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -5,4 +5,11 @@
namespace: "vcn"
description: "Feature flag for safe mode configurability"
bug: "276358140"
+}
+
+flag {
+ name: "safe_mode_timeout_config"
+ namespace: "vcn"
+ description: "Feature flag for adjustable safe mode timeout"
+ bug: "317406085"
}
\ No newline at end of file
diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java
index bb677d5..a0ab066 100644
--- a/core/java/android/os/PowerMonitorReadings.java
+++ b/core/java/android/os/PowerMonitorReadings.java
@@ -71,7 +71,7 @@
*/
@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
@ElapsedRealtimeLong
- public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
+ public long getTimestampMillis(@NonNull PowerMonitor powerMonitor) {
int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
if (offset >= 0) {
return mTimestampsMs[offset];
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index 2d53341..322a8e6 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -25,8 +25,8 @@
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IPowerStatsService;
+import android.os.OutcomeReceiver;
import android.os.PowerMonitor;
import android.os.PowerMonitorReadings;
import android.os.Process;
@@ -39,6 +39,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -161,12 +162,12 @@
* (on-device power rail monitor) rails and modeled energy consumers. If ODPM is unsupported
* on this device this method delivers an empty list.
*
- * @param handler optional Handler to deliver the callback. If not supplied, the callback
+ * @param executor optional Handler to deliver the callback. If not supplied, the callback
* may be invoked on an arbitrary thread.
* @param onResult callback for the result
*/
@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
- public void getSupportedPowerMonitors(@Nullable Handler handler,
+ public void getSupportedPowerMonitors(@Nullable Executor executor,
@NonNull Consumer<List<PowerMonitor>> onResult) {
final List<PowerMonitor> result;
synchronized (mPowerMonitorsLock) {
@@ -180,15 +181,15 @@
}
}
if (result != null) {
- if (handler != null) {
- handler.post(() -> onResult.accept(result));
+ if (executor != null) {
+ executor.execute(() -> onResult.accept(result));
} else {
onResult.accept(result);
}
return;
}
try {
- mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) {
+ mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
PowerMonitor[] array = resultData.getParcelableArray(
@@ -197,7 +198,11 @@
synchronized (mPowerMonitorsLock) {
mPowerMonitorsInfo = result;
}
- onResult.accept(result);
+ if (executor != null) {
+ executor.execute(()-> onResult.accept(result));
+ } else {
+ onResult.accept(result);
+ }
}
});
} catch (RemoteException e) {
@@ -213,17 +218,22 @@
* monitors.
*
* @param powerMonitors power monitors to be retrieved.
- * @param handler optional Handler to deliver the callbacks. If not supplied, the callback
- * may be invoked on an arbitrary thread.
- * @param onSuccess callback for the result
- * @param onError callback invoked in case of an error
+ * @param executor optional Executor to deliver the callbacks. If not supplied, the
+ * callback may be invoked on an arbitrary thread.
+ * @param onResult callback for the result
*/
@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
- @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
- @NonNull Consumer<RuntimeException> onError) {
+ @Nullable Executor executor,
+ @NonNull OutcomeReceiver<PowerMonitorReadings, RuntimeException> onResult) {
if (mPowerStats == null) {
- onError.accept(new IllegalArgumentException("Unsupported power monitor"));
+ IllegalArgumentException error =
+ new IllegalArgumentException("Unsupported power monitor");
+ if (executor != null) {
+ executor.execute(() -> onResult.onError(error));
+ } else {
+ onResult.onError(error);
+ }
return;
}
@@ -235,18 +245,31 @@
indices[i] = powerMonitorsArray[i].index;
}
try {
- mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) {
+ mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == IPowerStatsService.RESULT_SUCCESS) {
- onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray,
+ PowerMonitorReadings result = new PowerMonitorReadings(powerMonitorsArray,
resultData.getLongArray(IPowerStatsService.KEY_ENERGY),
- resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS)));
- } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
- onError.accept(new IllegalArgumentException("Unsupported power monitor"));
+ resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS));
+ if (executor != null) {
+ executor.execute(() -> onResult.onResult(result));
+ } else {
+ onResult.onResult(result);
+ }
} else {
- onError.accept(new IllegalStateException(
- "Unrecognized result code " + resultCode));
+ RuntimeException error;
+ if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) {
+ error = new IllegalArgumentException("Unsupported power monitor");
+ } else {
+ error = new IllegalStateException(
+ "Unrecognized result code " + resultCode);
+ }
+ if (executor != null) {
+ executor.execute(() -> onResult.onError(error));
+ } else {
+ onResult.onError(error);
+ }
}
}
});
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 7d9c0a3..2d657c2 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -445,6 +445,19 @@
}
/**
+ * Retrieves the current {@link android.app.Activity} associated with the dream.
+ * This method behaves similarly to calling {@link android.app.Activity#getActivity()}.
+ *
+ * @return The current activity, or null if the dream is not associated with an activity
+ * or not started.
+ *
+ * @hide
+ */
+ public Activity getActivity() {
+ return mActivity;
+ }
+
+ /**
* Inflates a layout resource and set it to be the content view for this Dream.
* Behaves similarly to {@link android.app.Activity#setContentView(int)}.
*
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 4c81888..a6d3bb4 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -454,7 +454,7 @@
line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
- mMax = (int) Math.ceil(line.metrics(null, null, false));
+ mMax = (int) Math.ceil(line.metrics(null, null, false, null));
TextLine.recycle(line);
}
@@ -603,7 +603,7 @@
0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
useFallbackLineSpacing);
- fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false));
+ fm.width = (int) Math.ceil(line.metrics(fm, fm.mDrawingBounds, false, null));
TextLine.recycle(line);
return fm;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c9906cc..eca848a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,6 +18,7 @@
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -50,8 +51,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.text.BreakIterator;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* A base class that manages text layout in visual elements on
@@ -669,7 +672,8 @@
int start = previousLineEnd;
previousLineEnd = getLineStart(lineNum + 1);
final boolean justify = isJustificationRequired(lineNum);
- int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
+ int end = getLineVisibleEnd(lineNum, start, previousLineEnd,
+ true /* trailingSpaceAtLastLineIsVisible */);
paint.setStartHyphenEdit(getStartHyphenEdit(lineNum));
paint.setEndHyphenEdit(getEndHyphenEdit(lineNum));
@@ -1056,7 +1060,7 @@
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
- tl.metrics(null, rectF, false);
+ tl.metrics(null, rectF, false, null);
float lineLeft = rectF.left;
float lineRight = rectF.right;
@@ -1456,7 +1460,7 @@
tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
- float wid = tl.measure(offset - start, trailing, null, null);
+ float wid = tl.measure(offset - start, trailing, null, null, null);
TextLine.recycle(tl);
if (clamped && wid > mWidth) {
@@ -1792,12 +1796,69 @@
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
- final float width = tl.metrics(null, null, mUseBoundsForWidth);
+ final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
TextLine.recycle(tl);
return width;
}
/**
+ * Returns the number of letter spacing unit in the line.
+ *
+ * <p>
+ * This API returns a number of letters that is a target of letter spacing. The letter spacing
+ * won't be added to the middle of the characters that are needed to be treated as a single,
+ * e.g., ligatured or conjunct form. Note that this value is different from the number of]
+ * grapheme clusters that is calculated by {@link BreakIterator#getCharacterInstance(Locale)}.
+ * For example, if the "fi" is ligatured, the ligatured form is treated as single uni and letter
+ * spacing is not added, but it has two separate grapheme cluster.
+ *
+ * <p>
+ * This value is used for calculating the letter spacing amount for the justification because
+ * the letter spacing is applied between clusters. For example, if extra {@code W} pixels needed
+ * to be filled by letter spacing, the amount of letter spacing to be applied is
+ * {@code W}/(letter spacing unit count - 1) px.
+ *
+ * @param line the index of the line
+ * @param includeTrailingWhitespace whether to include trailing whitespace
+ * @return the number of cluster count in the line.
+ */
+ @IntRange(from = 0)
+ @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line,
+ boolean includeTrailingWhitespace) {
+ final int start = getLineStart(line);
+ final int end = includeTrailingWhitespace ? getLineEnd(line)
+ : getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
+ false // trailingSpaceAtLastLineIsVisible: Treating trailing whitespaces at
+ // the last line as a invisible chars for single line justification.
+ );
+
+ final Directions directions = getLineDirections(line);
+ // Returned directions can actually be null
+ if (directions == null) {
+ return 0;
+ }
+ final int dir = getParagraphDirection(line);
+
+ final TextLine tl = TextLine.obtain();
+ final TextPaint paint = mWorkPaint;
+ paint.set(mPaint);
+ paint.setStartHyphenEdit(getStartHyphenEdit(line));
+ paint.setEndHyphenEdit(getEndHyphenEdit(line));
+ tl.set(paint, mText, start, end, dir, directions,
+ false, null, // tab width is not used for cluster counting.
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
+ if (mLineInfo == null) {
+ mLineInfo = new TextLine.LineInfo();
+ }
+ mLineInfo.setClusterCount(0);
+ tl.metrics(null, null, mUseBoundsForWidth, mLineInfo);
+ TextLine.recycle(tl);
+ return mLineInfo.getClusterCount();
+ }
+
+ /**
* Returns the signed horizontal extent of the specified line, excluding
* leading margin. If full is false, excludes trailing whitespace.
* @param line the index of the line
@@ -1823,7 +1884,7 @@
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
- final float width = tl.metrics(null, null, mUseBoundsForWidth);
+ final float width = tl.metrics(null, null, mUseBoundsForWidth, null);
TextLine.recycle(tl);
return width;
}
@@ -2432,14 +2493,21 @@
* is not counted) on the specified line.
*/
public int getLineVisibleEnd(int line) {
- return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
+ return getLineVisibleEnd(line, getLineStart(line), getLineStart(line + 1),
+ true /* trailingSpaceAtLastLineIsVisible */);
}
- private int getLineVisibleEnd(int line, int start, int end) {
+ private int getLineVisibleEnd(int line, int start, int end,
+ boolean trailingSpaceAtLastLineIsVisible) {
CharSequence text = mText;
char ch;
- if (line == getLineCount() - 1) {
- return end;
+
+ // Historically, trailing spaces at the last line is counted as visible. However, this
+ // doesn't work well for justification.
+ if (trailingSpaceAtLastLineIsVisible) {
+ if (line == getLineCount() - 1) {
+ return end;
+ }
}
for (; end > start; end--) {
@@ -2939,7 +3007,7 @@
tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
0 /* ellipsisStart */, 0 /* ellipsisEnd */,
false /* use fallback line spacing. unused */);
- return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth));
+ return margin + Math.abs(tl.metrics(null, null, useBoundsForWidth, null));
} finally {
TextLine.recycle(tl);
if (mt != null) {
@@ -3337,6 +3405,8 @@
private boolean mUseBoundsForWidth;
private @Nullable Paint.FontMetrics mMinimumFontMetrics;
+ private TextLine.LineInfo mLineInfo = null;
+
/** @hide */
@IntDef(prefix = { "DIR_" }, value = {
DIR_LEFT_TO_RIGHT,
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index f9abec0..135935c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -76,6 +76,21 @@
private RectF mTmpRectForPaintAPI;
private Rect mTmpRectForPrecompute;
+ // Recycling object for Paint APIs. Do not use outside getRunAdvances method.
+ private Paint.RunInfo mRunInfo;
+
+ public static final class LineInfo {
+ private int mClusterCount;
+
+ public int getClusterCount() {
+ return mClusterCount;
+ }
+
+ public void setClusterCount(int clusterCount) {
+ mClusterCount = clusterCount;
+ }
+ };
+
private boolean mUseFallbackExtent = false;
// The start and end of a potentially existing ellipsis on this text line.
@@ -270,7 +285,7 @@
// width.
return;
}
- final float width = Math.abs(measure(end, false, null, null));
+ final float width = Math.abs(measure(end, false, null, null, null));
mAddedWidthForJustify = (justifyWidth - width) / spaces;
mIsJustifying = true;
}
@@ -315,10 +330,12 @@
* @param drawBounds output parameter for drawing bounding box. optional.
* @param returnDrawWidth true for returning width of the bounding box, false for returning
* total advances.
+ * @param lineInfo an optional output parameter for filling line information.
* @return the signed width of the line
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth) {
+ public float metrics(FontMetricsInt fmi, @Nullable RectF drawBounds, boolean returnDrawWidth,
+ @Nullable LineInfo lineInfo) {
if (returnDrawWidth) {
if (drawBounds == null) {
if (mTmpRectForMeasure == null) {
@@ -327,7 +344,7 @@
drawBounds = mTmpRectForMeasure;
}
drawBounds.setEmpty();
- float w = measure(mLen, false, fmi, drawBounds);
+ float w = measure(mLen, false, fmi, drawBounds, lineInfo);
float boundsWidth = drawBounds.width();
if (Math.abs(w) > boundsWidth) {
return w;
@@ -337,7 +354,7 @@
return Math.signum(w) * boundsWidth;
}
} else {
- return measure(mLen, false, fmi, drawBounds);
+ return measure(mLen, false, fmi, drawBounds, lineInfo);
}
}
@@ -407,12 +424,13 @@
* the edge of the preceding run's edge. See example above.
* @param fmi receives metrics information about the requested character, can be null
* @param drawBounds output parameter for drawing bounding box. optional.
+ * @param lineInfo an optional output parameter for filling line information.
* @return the signed graphical offset from the leading margin to the requested character edge.
* The positive value means the offset is right from the leading edge. The negative
* value means the offset is left from the leading edge.
*/
public float measure(@IntRange(from = 0) int offset, boolean trailing,
- @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds) {
+ @NonNull FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable LineInfo lineInfo) {
if (offset > mLen) {
throw new IndexOutOfBoundsException(
"offset(" + offset + ") should be less than line limit(" + mLen + ")");
@@ -437,16 +455,16 @@
if (targetIsInThisSegment && sameDirection) {
return h + measureRun(segStart, offset, j, runIsRtl, fmi, drawBounds, null,
- 0, h);
+ 0, h, lineInfo);
}
final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, drawBounds,
- null, 0, h);
+ null, 0, h, lineInfo);
h += sameDirection ? segmentWidth : -segmentWidth;
if (targetIsInThisSegment) {
return h + measureRun(segStart, offset, j, runIsRtl, null, null, null, 0,
- h);
+ h, lineInfo);
}
if (j != runLimit) { // charAt(j) == TAB_CHAR
@@ -537,7 +555,8 @@
final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
final float segmentWidth =
- measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0);
+ measureRun(segStart, j, j, runIsRtl, null, null, advances, segStart, 0,
+ null);
final float oldh = h;
h += sameDirection ? segmentWidth : -segmentWidth;
@@ -578,7 +597,7 @@
}
/**
- * @see #measure(int, boolean, FontMetricsInt, RectF)
+ * @see #measure(int, boolean, FontMetricsInt, RectF, LineInfo)
* @return The measure results for all possible offsets
*/
@VisibleForTesting
@@ -610,7 +629,7 @@
final float previousSegEndHorizontal = measurement[segStart];
final float width =
measureRun(segStart, j, j, runIsRtl, fmi, null, measurement, segStart,
- 0);
+ 0, null);
horizontal += sameDirection ? width : -width;
float currHorizontal = sameDirection ? oldHorizontal : horizontal;
@@ -675,14 +694,14 @@
boolean needWidth) {
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
- float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
+ float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
- y, bottom, null, null, false, null, 0);
+ y, bottom, null, null, false, null, 0, null);
return w;
}
return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
- y, bottom, null, null, needWidth, null, 0);
+ y, bottom, null, null, needWidth, null, 0, null);
}
/**
@@ -698,19 +717,20 @@
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
* @param x horizontal offset of the run.
+ * @param lineInfo an optional output parameter for filling line information.
* @return the signed width from the start of the run to the leading edge
* of the character at offset, based on the run (not paragraph) direction
*/
private float measureRun(int start, int offset, int limit, boolean runIsRtl,
@Nullable FontMetricsInt fmi, @Nullable RectF drawBounds, @Nullable float[] advances,
- int advancesIndex, float x) {
+ int advancesIndex, float x, @Nullable LineInfo lineInfo) {
if (drawBounds != null && (mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
- float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0);
+ float w = -measureRun(start, offset, limit, runIsRtl, null, null, null, 0, 0, null);
return handleRun(start, offset, limit, runIsRtl, null, null, x + w, 0, 0, 0, fmi,
- drawBounds, true, advances, advancesIndex);
+ drawBounds, true, advances, advancesIndex, lineInfo);
}
return handleRun(start, offset, limit, runIsRtl, null, null, x, 0, 0, 0, fmi, drawBounds,
- true, advances, advancesIndex);
+ true, advances, advancesIndex, lineInfo);
}
/**
@@ -729,14 +749,14 @@
int limit, boolean runIsRtl, float x, boolean needWidth) {
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
- float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0);
+ float w = -measureRun(start, limit, limit, runIsRtl, null, null, null, 0, 0, null);
handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, null,
- false, null, 0);
+ false, null, 0, null);
return w;
}
return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, null,
- needWidth, null, 0);
+ needWidth, null, 0, null);
}
@@ -1077,16 +1097,35 @@
private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex,
- RectF drawingBounds) {
+ RectF drawingBounds, @Nullable LineInfo lineInfo) {
+ if (lineInfo != null) {
+ if (mRunInfo == null) {
+ mRunInfo = new Paint.RunInfo();
+ }
+ mRunInfo.setClusterCount(0);
+ } else {
+ mRunInfo = null;
+ }
if (mCharsValid) {
- return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
- runIsRtl, offset, advances, advancesIndex, drawingBounds);
+ float r = wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
+ runIsRtl, offset, advances, advancesIndex, drawingBounds, mRunInfo);
+ if (lineInfo != null) {
+ lineInfo.setClusterCount(lineInfo.getClusterCount() + mRunInfo.getClusterCount());
+ }
+ return r;
} else {
final int delta = mStart;
- if (mComputed == null || advances != null) {
- return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
+ // TODO: Add cluster information to the PrecomputedText for better performance of
+ // justification.
+ if (mComputed == null || advances != null || lineInfo != null) {
+ float r = wp.getRunCharacterAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl,
- delta + offset, advances, advancesIndex, drawingBounds);
+ delta + offset, advances, advancesIndex, drawingBounds, mRunInfo);
+ if (lineInfo != null) {
+ lineInfo.setClusterCount(
+ lineInfo.getClusterCount() + mRunInfo.getClusterCount());
+ }
+ return r;
} else {
if (drawingBounds != null) {
if (mTmpRectForPrecompute == null) {
@@ -1120,6 +1159,7 @@
* @param decorations the list of locations and paremeters for drawing decorations
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
+ * @param lineInfo an optional output parameter for filling line information.
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
@@ -1128,7 +1168,7 @@
Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
FontMetricsInt fmi, RectF drawBounds, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations,
- @Nullable float[] advances, int advancesIndex) {
+ @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {
if (mIsJustifying) {
wp.setWordSpacing(mAddedWidthForJustify);
@@ -1155,7 +1195,8 @@
mTmpRectForPaintAPI = new RectF();
}
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
- advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI);
+ advances, advancesIndex, drawBounds == null ? null : mTmpRectForPaintAPI,
+ lineInfo);
if (drawBounds != null) {
if (runIsRtl) {
mTmpRectForPaintAPI.offset(x - totalWidth, 0);
@@ -1206,9 +1247,9 @@
final int decorationStart = Math.max(info.start, start);
final int decorationEnd = Math.min(info.end, offset);
float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
- contextEnd, runIsRtl, decorationStart, null, 0, null);
+ contextEnd, runIsRtl, decorationStart, null, 0, null, null);
float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
- contextEnd, runIsRtl, decorationEnd, null, 0, null);
+ contextEnd, runIsRtl, decorationEnd, null, 0, null, null);
final float decorationXLeft, decorationXRight;
if (runIsRtl) {
decorationXLeft = rightX - decorationEndAdvance;
@@ -1377,6 +1418,7 @@
* @param needWidth true if the width is required
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
+ * @param lineInfo an optional output parameter for filling line information.
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
@@ -1384,7 +1426,7 @@
int limit, boolean runIsRtl, Canvas c,
TextShaper.GlyphsConsumer consumer, float x, int top, int y,
int bottom, FontMetricsInt fmi, RectF drawBounds, boolean needWidth,
- @Nullable float[] advances, int advancesIndex) {
+ @Nullable float[] advances, int advancesIndex, @Nullable LineInfo lineInfo) {
if (measureLimit < start || measureLimit > limit) {
throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
@@ -1431,7 +1473,7 @@
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
y, bottom, fmi, drawBounds, needWidth, measureLimit, null, advances,
- advancesIndex);
+ advancesIndex, lineInfo);
}
// Shaping needs to take into account context up to metric boundaries,
@@ -1523,7 +1565,7 @@
consumer, x, top, y, bottom, fmi, drawBounds,
needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations,
- advances, advancesIndex + activeStart - start);
+ advances, advancesIndex + activeStart - start, lineInfo);
activeStart = j;
activePaint.set(wp);
@@ -1551,7 +1593,7 @@
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
top, y, bottom, fmi, drawBounds, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations,
- advances, advancesIndex + activeStart - start);
+ advances, advancesIndex + activeStart - start, lineInfo);
}
return x - originalX;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index ef50045..1d2f653 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,6 +16,9 @@
package android.view.textclassifier;
+import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -108,6 +111,9 @@
String TYPE_DATE_TIME = "datetime";
/** Flight number in IATA format. */
String TYPE_FLIGHT_NUMBER = "flight";
+ /** One-time login codes */
+ @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
+ String TYPE_OTP_CODE = "otp_code";
/**
* Word that users may be interested to look up for meaning.
* @hide
@@ -126,7 +132,8 @@
TYPE_DATE,
TYPE_DATE_TIME,
TYPE_FLIGHT_NUMBER,
- TYPE_DICTIONARY
+ TYPE_DICTIONARY,
+ TYPE_OTP_CODE
})
@interface EntityType {}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f2bce9c..bb16ad2 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -72,7 +72,7 @@
name: "predictive_back_system_animations"
namespace: "systemui"
description: "Predictive back for system animations"
- bug: "309545085"
+ bug: "319421778"
is_fixed_read_only: true
}
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index b651711..a5b2f65 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -96,8 +96,11 @@
static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) {
std::string error;
+ // Use temporary VintfObject, not the shared instance, to release memory
+ // after check.
int32_t status =
- VintfObject::GetInstance()
+ VintfObject::Builder()
+ .build()
->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel());
if (status)
LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ef6caef..5f3f641 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5214,6 +5214,14 @@
<permission android:name="android.permission.BIND_REMOTE_DISPLAY"
android:protectionLevel="signature" />
+ <!-- Must be required by a android.media.tv.ad.TvAdService to ensure that only the system can
+ bind to it.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.media.tv.flags.enable_ad_service_fw")
+ -->
+ <permission android:name="android.permission.BIND_TV_AD_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.media.tv.TvInputService}
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 542e9d6..f285806 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1825,7 +1825,7 @@
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Press firmly on the sensor</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
- <string name="fingerprint_acquired_insufficient">Can\u2019t recognize fingerprint. Try again.</string>
+ <string name="fingerprint_acquired_insufficient">Fingerprint not recognized. Try again.</string>
<!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning -->
<string name="fingerprint_acquired_imager_dirty">Clean fingerprint sensor and try again</string>
<string name="fingerprint_acquired_imager_dirty_alt">Clean sensor and try again</string>
@@ -1959,7 +1959,7 @@
<!-- Message shown during face acquisition when the sensor needs to be recalibrated [CHAR LIMIT=50] -->
<string name="face_acquired_recalibrate">Please re-enroll your face.</string>
<!-- Message shown during face enrollment when a different person's face is detected [CHAR LIMIT=50] -->
- <string name="face_acquired_too_different">Can\u2019t recognize face. Try again.</string>
+ <string name="face_acquired_too_different">Face not recognized. Try again.</string>
<!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] -->
<string name="face_acquired_too_similar">Change the position of your head slightly</string>
<!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] -->
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index bf56df1..0dec756 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertNotEquals;
import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
@@ -362,4 +363,44 @@
// = 30
assertEquals(30.0f, p.getUnderlineThickness(), 0.5f);
}
+
+ private int getClusterCount(Paint p, String text) {
+ Paint.RunInfo runInfo = new Paint.RunInfo();
+ p.getRunCharacterAdvance(text, 0, text.length(), 0, text.length(), false, 0, null, 0, null,
+ runInfo);
+ int ccByString = runInfo.getClusterCount();
+ runInfo.setClusterCount(0);
+ char[] buf = new char[text.length()];
+ TextUtils.getChars(text, 0, text.length(), buf, 0);
+ p.getRunCharacterAdvance(buf, 0, buf.length, 0, buf.length, false, 0, null, 0, null,
+ runInfo);
+ int ccByChars = runInfo.getClusterCount();
+ assertEquals(ccByChars, ccByString);
+ return ccByChars;
+ }
+
+ public void testCluster() {
+ final Paint p = new Paint();
+ p.setTextSize(100);
+
+ // Regular String
+ assertEquals(1, getClusterCount(p, "A"));
+ assertEquals(2, getClusterCount(p, "AB"));
+
+ // Ligature is in the same cluster
+ assertEquals(1, getClusterCount(p, "fi")); // Ligature
+ p.setFontFeatureSettings("'liga' off");
+ assertEquals(2, getClusterCount(p, "fi")); // Ligature is disabled
+ p.setFontFeatureSettings("");
+
+ // Combining character
+ assertEquals(1, getClusterCount(p, "\u0061\u0300")); // A + COMBINING GRAVE ACCENT
+
+ // BiDi
+ final String rtlStr = "\u05D0\u05D1\u05D2";
+ final String ltrStr = "abc";
+ assertEquals(3, getClusterCount(p, rtlStr));
+ assertEquals(6, getClusterCount(p, rtlStr + ltrStr));
+ assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr));
+ }
}
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 34842a0..a31992c 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -50,11 +50,11 @@
tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */,
0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
- final float originalWidth = tl.metrics(null, null, false);
+ final float originalWidth = tl.metrics(null, null, false, null);
final float expandedWidth = 2 * originalWidth;
tl.justify(expandedWidth);
- final float newWidth = tl.metrics(null, null, false);
+ final float newWidth = tl.metrics(null, null, false, null);
TextLine.recycle(tl);
return Math.abs(newWidth - expandedWidth) < 0.5;
}
@@ -128,7 +128,7 @@
private void assertMeasurements(final TextLine tl, final int length, boolean trailing,
final float[] expected) {
for (int offset = 0; offset <= length; ++offset) {
- assertEquals(expected[offset], tl.measure(offset, trailing, null, null), 0.0f);
+ assertEquals(expected[offset], tl.measure(offset, trailing, null, null, null), 0.0f);
}
final boolean[] trailings = new boolean[length + 1];
@@ -318,7 +318,7 @@
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
- tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+ tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
assertFalse(span.mIsUsed);
}
@@ -335,7 +335,7 @@
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
- tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+ tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
assertTrue(span.mIsUsed);
}
@@ -352,7 +352,7 @@
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
- tl.measure(text.length(), false /* trailing */, null /* fmi */, null);
+ tl.measure(text.length(), false /* trailing */, null /* fmi */, null, null);
assertTrue(span.mIsUsed);
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index f10cdb8..c5a2f98 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -65,6 +65,8 @@
private long mNativeShader;
private long mNativeColorFilter;
+ private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric");
+
// Use a Holder to allow static initialization of Paint in the boot image.
private static class NoImagePreloadHolder {
public static final NativeAllocationRegistry sRegistry =
@@ -2474,6 +2476,19 @@
nGetFontMetricsInt(mNativePaint, metrics, true);
}
+ /** @hide */
+ public static final class RunInfo {
+ private int mClusterCount = 0;
+
+ public int getClusterCount() {
+ return mClusterCount;
+ }
+
+ public void setClusterCount(int clusterCount) {
+ mClusterCount = clusterCount;
+ }
+ }
+
/**
* Return the recommend line spacing based on the current typeface and
* text size.
@@ -3320,7 +3335,7 @@
int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
- advances, advancesIndex, null);
+ advances, advancesIndex, null, null);
}
/**
@@ -3339,12 +3354,14 @@
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @param drawBounds the output parameter for the bounding box of drawing text, optional
+ * @param runInfo the output parameter for storing run information.
* @return width measurement between start and offset
- * @hide
+ * @hide TODO: Reorganize APIs
*/
public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset,
- @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+ @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+ @Nullable RunInfo runInfo) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -3370,11 +3387,19 @@
}
if (end == start) {
+ if (runInfo != null) {
+ runInfo.setClusterCount(0);
+ }
return 0.0f;
}
- return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
- isRtl, offset, advances, advancesIndex, drawBounds);
+ if (sIsRobolectric) {
+ return nGetRunCharacterAdvance(mNativePaint, text, start, end,
+ contextStart, contextEnd, isRtl, offset, advances, advancesIndex, drawBounds);
+ } else {
+ return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
+ isRtl, offset, advances, advancesIndex, drawBounds, runInfo);
+ }
}
/**
@@ -3402,7 +3427,7 @@
int contextStart, int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
return getRunCharacterAdvance(text, start, end, contextStart, contextEnd, isRtl, offset,
- advances, advancesIndex, null);
+ advances, advancesIndex, null, null);
}
/**
@@ -3418,12 +3443,14 @@
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @param drawBounds the output parameter for the bounding box of drawing text, optional
+ * @param runInfo an optional output parameter for filling run information.
* @return width measurement between start and offset
- * @hide
+ * @hide TODO: Reorganize APIs
*/
public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, int offset,
- @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds) {
+ @Nullable float[] advances, int advancesIndex, @Nullable RectF drawBounds,
+ @Nullable RunInfo runInfo) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -3456,7 +3483,7 @@
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
0, contextEnd - contextStart, isRtl, offset - contextStart,
- advances, advancesIndex, drawBounds);
+ advances, advancesIndex, drawBounds, runInfo);
TemporaryBuffer.recycle(buf);
return result;
}
@@ -3574,7 +3601,7 @@
int contextStart, int contextEnd, boolean isRtl, int offset);
private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
- int advancesIndex, RectF drawingBounds);
+ int advancesIndex, RectF drawingBounds, RunInfo runInfo);
private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float advance);
private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
@@ -3729,4 +3756,11 @@
private static native void nSetTextSize(long paintPtr, float textSize);
@CriticalNative
private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
+
+
+ // Following Native methods are kept for old Robolectric JNI signature used by
+ // SystemUIGoogleRoboRNGTests
+ private static native float nGetRunCharacterAdvance(long paintPtr, char[] text,
+ int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset,
+ float[] advances, int advancesIndex, RectF drawingBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 80fc3a8..ac2a1c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -136,6 +136,9 @@
mStartTaskRect.set(mClosingTarget.windowConfiguration.getBounds());
mStartTaskRect.offsetTo(0, 0);
+ // inset bottom in case of pinned taskbar being present
+ mStartTaskRect.inset(0, 0, 0, mClosingTarget.contentInsets.bottom);
+
// Draw background.
mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
BACKGROUNDCOLOR, mTransaction);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index bc1a575..5de8a9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -481,18 +481,20 @@
private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) {
final float end = show ? 1.f : 0.f;
final float start = 1.f - end;
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(start, end);
va.setDuration(FADE_DURATION);
va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT);
va.addUpdateListener(animation -> {
float fraction = animation.getAnimatedFraction();
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
transaction.apply();
+ mTransactionPool.release(transaction);
});
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
transaction.setAlpha(leash, end);
transaction.apply();
mTransactionPool.release(transaction);
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index 182a908..be77171 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -101,7 +101,8 @@
override fun pipLayerReduces() {
flicker.assertLayers {
val pipLayerList =
- this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible }
+ this.layers { standardAppHelper.packageNameMatcher.layerMatchesAnyOf(it)
+ && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 2f28363..77800a3 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -31,6 +31,12 @@
],
}
+cc_aconfig_library {
+ name: "backup_flags_cc_lib",
+ host_supported: true,
+ aconfig_declarations: "backup_flags",
+}
+
cc_defaults {
name: "libandroidfw_defaults",
cpp_std: "gnu++2b",
@@ -115,7 +121,10 @@
"libutils",
"libz",
],
- static_libs: ["libziparchive_for_incfs"],
+ static_libs: [
+ "libziparchive_for_incfs",
+ "backup_flags_cc_lib",
+ ],
static: {
enabled: false,
},
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 1a6a952..a1e7c2f 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -36,6 +36,9 @@
#include <utils/KeyedVector.h>
#include <utils/String8.h>
+#include <com_android_server_backup.h>
+namespace backup_flags = com::android::server::backup;
+
namespace android {
#define MAGIC0 0x70616e53 // Snap
@@ -214,7 +217,7 @@
{
LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.c_str(), mode);
- const int bufsize = 4*1024;
+ const int bufsize = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (4*1024);
int err;
int amt;
int fileSize;
@@ -550,7 +553,8 @@
}
// read/write up to this much at a time.
- const size_t BUFSIZE = 32 * 1024;
+ const size_t BUFSIZE = backup_flags::enable_max_size_writes_to_pipes() ? (64*1024) : (32*1024);
+
char* buf = (char *)calloc(1,BUFSIZE);
const size_t PAXHEADER_OFFSET = 512;
const size_t PAXHEADER_SIZE = 512;
@@ -726,7 +730,7 @@
-#define RESTORE_BUF_SIZE (8*1024)
+const size_t RESTORE_BUF_SIZE = backup_flags::enable_max_size_writes_to_pipes() ? 64*1024 : 8*1024;
RestoreHelperBase::RestoreHelperBase()
{
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index c156c46..72ddecc 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -22,6 +22,13 @@
}
flag {
+ name: "high_contrast_text_small_text_rect"
+ namespace: "accessibility"
+ description: "Draw a solid rectangle background behind text instead of a stroke outline"
+ bug: "186567103"
+}
+
+flag {
name: "hdr_10bit_plus"
namespace: "core_graphics"
description: "Use 10101010 and FP16 formats for HDR-UI when available"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 80b6c03..e9f4b81c 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -18,6 +18,7 @@
#include <SkFontMetrics.h>
#include <SkRRect.h>
+#include <minikin/MinikinRect.h>
#include "FeatureFlags.h"
#include "MinikinUtils.h"
@@ -107,7 +108,13 @@
// care of all alignment.
paint.setTextAlign(Paint::kLeft_Align);
- DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
+ minikin::MinikinRect bounds;
+ // We only need the bounds to draw a rectangular background in high contrast mode. Let's save
+ // the cycles otherwise.
+ if (flags::high_contrast_text_small_text_rect() && isHighContrastText()) {
+ MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, textSize, &bounds);
+ }
+ DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance(), bounds);
MinikinUtils::forFontRun(layout, &paint, f);
if (text_feature::fix_double_underline()) {
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 8f99990..ba65439 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -33,6 +33,8 @@
namespace android {
+inline constexpr int kHighContrastTextBorderWidth = 4;
+
static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
const Paint& paint, Canvas* canvas) {
const SkScalar strokeWidth = fmax(thickness, 1.0f);
@@ -45,15 +47,26 @@
paint->setShader(nullptr);
paint->setColorFilter(nullptr);
paint->setLooper(nullptr);
- paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
+ paint->setStrokeWidth(kHighContrastTextBorderWidth + 0.04 * paint->getSkFont().getSize());
paint->setStrokeJoin(SkPaint::kRound_Join);
paint->setLooper(nullptr);
}
class DrawTextFunctor {
public:
+ /**
+ * Creates a Functor to draw the given text layout.
+ *
+ * @param layout
+ * @param canvas
+ * @param paint
+ * @param x
+ * @param y
+ * @param totalAdvance
+ * @param bounds bounds of the text. Only required if high contrast text mode is enabled.
+ */
DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
- float y, float totalAdvance)
+ float y, float totalAdvance, const minikin::MinikinRect& bounds)
: layout(layout)
, canvas(canvas)
, paint(paint)
@@ -61,7 +74,8 @@
, y(y)
, totalAdvance(totalAdvance)
, underlinePosition(0)
- , underlineThickness(0) {}
+ , underlineThickness(0)
+ , bounds(bounds) {}
void operator()(size_t start, size_t end) {
auto glyphFunc = [&](uint16_t* text, float* positions) {
@@ -91,7 +105,16 @@
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+ if (flags::high_contrast_text_small_text_rect()) {
+ auto bgBounds(bounds);
+ auto padding = kHighContrastTextBorderWidth + 0.1f * paint.getSkFont().getSize();
+ bgBounds.offset(x, y);
+ canvas->drawRect(bgBounds.mLeft - padding, bgBounds.mTop - padding,
+ bgBounds.mRight + padding, bgBounds.mBottom + padding,
+ outlinePaint);
+ } else {
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+ }
// inner
gDrawTextBlobMode = DrawTextBlobMode::HctInner;
@@ -146,6 +169,7 @@
float totalAdvance;
float underlinePosition;
float underlineThickness;
+ const minikin::MinikinRect& bounds;
};
} // namespace android
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 7552b56d..833069f 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -96,7 +96,7 @@
float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances,
- minikin::MinikinRect* bounds) {
+ minikin::MinikinRect* bounds, uint32_t* clusterCount) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
const minikin::U16StringPiece textBuf(buf, bufSize);
const minikin::Range range(start, start + count);
@@ -104,7 +104,7 @@
const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
- endHyphen, advances, bounds);
+ endHyphen, advances, bounds, clusterCount);
}
minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 61bc881..f8574ee 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -53,7 +53,7 @@
static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
const uint16_t* buf, size_t start, size_t count, size_t bufSize,
- float* advances, minikin::MinikinRect* bounds);
+ float* advances, minikin::MinikinRect* bounds, uint32_t* clusterCount);
static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 7cc4866..8315c4c 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -247,6 +247,9 @@
static jfieldID gFontMetricsInt_bottom;
static jfieldID gFontMetricsInt_leading;
+static jclass gRunInfo_class;
+static jfieldID gRunInfo_clusterCount;
+
///////////////////////////////////////////////////////////////////////////////
void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -511,6 +514,10 @@
return descent - ascent + leading;
}
+void GraphicsJNI::set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount) {
+ env->SetIntField(runInfo, gRunInfo_clusterCount, clusterCount);
+}
+
///////////////////////////////////////////////////////////////////////////////////////////
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoderWrapper* bitmap) {
@@ -834,5 +841,10 @@
gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+ gRunInfo_class = FindClassOrDie(env, "android/graphics/Paint$RunInfo");
+ gRunInfo_class = MakeGlobalRefOrDie(env, gRunInfo_class);
+
+ gRunInfo_clusterCount = GetFieldIDOrDie(env, gRunInfo_class, "mClusterCount", "I");
+
return 0;
}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index b9fff36..b0a1074 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -77,6 +77,8 @@
static SkRect* jrect_to_rect(JNIEnv*, jobject jrect, SkRect*);
static void rect_to_jrectf(const SkRect&, JNIEnv*, jobject jrectf);
+ static void set_cluster_count_to_run_info(JNIEnv* env, jobject runInfo, jint clusterCount);
+
static void set_jpoint(JNIEnv*, jobject jrect, int x, int y);
static SkIPoint* jpoint_to_ipoint(JNIEnv*, jobject jpoint, SkIPoint* point);
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index d84b73d..58d9d8b 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -114,7 +114,7 @@
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0,
- count, count, advancesArray.get(), nullptr);
+ count, count, advancesArray.get(), nullptr, nullptr);
for (int i = 0; i < count; i++) {
// traverse in the given direction
@@ -206,7 +206,7 @@
}
const float advance = MinikinUtils::measureText(
paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count,
- contextCount, advancesArray.get(), nullptr);
+ contextCount, advancesArray.get(), nullptr, nullptr);
if (advances) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
@@ -244,7 +244,7 @@
minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
- advancesArray.get(), nullptr);
+ advancesArray.get(), nullptr, nullptr);
size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
start, count, offset, moveOpt);
return static_cast<jint>(result);
@@ -508,7 +508,7 @@
static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface,
const jchar buf[], jint start, jint count, jint bufSize,
jboolean isRtl, jint offset, jfloatArray advances,
- jint advancesIndex, SkRect* drawBounds) {
+ jint advancesIndex, SkRect* drawBounds, uint32_t* clusterCount) {
if (advances) {
size_t advancesLength = env->GetArrayLength(advances);
if ((size_t)(count + advancesIndex) > advancesLength) {
@@ -519,9 +519,9 @@
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
minikin::MinikinRect bounds;
if (offset == start + count && advances == nullptr) {
- float result =
- MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
- bufSize, nullptr, drawBounds ? &bounds : nullptr);
+ float result = MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
+ bufSize, nullptr,
+ drawBounds ? &bounds : nullptr, clusterCount);
if (drawBounds) {
copyMinikinRectToSkRect(bounds, drawBounds);
}
@@ -529,7 +529,8 @@
}
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
- advancesArray.get(), drawBounds ? &bounds : nullptr);
+ advancesArray.get(), drawBounds ? &bounds : nullptr,
+ clusterCount);
if (drawBounds) {
copyMinikinRectToSkRect(bounds, drawBounds);
@@ -549,7 +550,7 @@
ScopedCharArrayRO textArray(env, text);
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
- isRtl, offset - contextStart, nullptr, 0, nullptr);
+ isRtl, offset - contextStart, nullptr, 0, nullptr, nullptr);
return result;
}
@@ -558,27 +559,41 @@
jint contextStart, jint contextEnd,
jboolean isRtl, jint offset,
jfloatArray advances, jint advancesIndex,
- jobject drawBounds) {
+ jobject drawBounds, jobject runInfo) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
ScopedCharArrayRO textArray(env, text);
SkRect skDrawBounds;
+ uint32_t clusterCount = 0;
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
isRtl, offset - contextStart, advances, advancesIndex,
- drawBounds ? &skDrawBounds : nullptr);
+ drawBounds ? &skDrawBounds : nullptr, &clusterCount);
if (drawBounds != nullptr) {
GraphicsJNI::rect_to_jrectf(skDrawBounds, env, drawBounds);
}
+ if (runInfo) {
+ GraphicsJNI::set_cluster_count_to_run_info(env, runInfo, clusterCount);
+ }
return result;
}
+ // This method is kept for old Robolectric JNI signature used by SystemUIGoogleRoboRNGTests.
+ static jfloat getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric(
+ JNIEnv* env, jclass cls, jlong paintHandle, jcharArray text, jint start, jint end,
+ jint contextStart, jint contextEnd, jboolean isRtl, jint offset, jfloatArray advances,
+ jint advancesIndex, jobject drawBounds) {
+ return getRunCharacterAdvance___CIIIIZI_FI_F(env, cls, paintHandle, text, start, end,
+ contextStart, contextEnd, isRtl, offset,
+ advances, advancesIndex, drawBounds, nullptr);
+ }
+
static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
- advancesArray.get(), nullptr);
+ advancesArray.get(), nullptr, nullptr);
return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
}
@@ -1145,8 +1160,11 @@
(void*)PaintGlue::getCharArrayBounds},
{"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph},
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F},
- {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F",
+ {"nGetRunCharacterAdvance",
+ "(J[CIIIIZI[FILandroid/graphics/RectF;Landroid/graphics/Paint$RunInfo;)F",
(void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F},
+ {"nGetRunCharacterAdvance", "(J[CIIIIZI[FILandroid/graphics/RectF;)F",
+ (void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F_ForRobolectric},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I},
{"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
(void*)PaintGlue::getFontMetricsIntForText___C},
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index c70a304..9911bfa 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -103,8 +103,9 @@
// Create minikin::Layout
std::unique_ptr<Typeface> typeface(makeTypeface());
minikin::Layout layout = doLayout(text, *paint, typeface.get());
+ minikin::MinikinRect bounds;
- DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance());
+ DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance(), bounds);
MinikinUtils::forFontRun(layout, paint, f);
return f;
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index ee2510f..0d5af50 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.SystemClock;
import android.telephony.TelephonyCallback;
@@ -26,6 +27,8 @@
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.concurrent.TimeUnit;
/**
@@ -139,8 +142,20 @@
(mCallEndElapsedRealtimeMillis > 0)
&& ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis)
< emergencyExtensionMillis);
- boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
- boolean isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ boolean isInEmergencyCallback = false;
+ boolean isInEmergencySmsMode = false;
+ if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+ isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+ isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ } else {
+ PackageManager pm = mContext.getPackageManager();
+ if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
+ }
+ if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ }
+ }
return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension
|| isInEmergencySmsMode;
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 89792c7..8f8cd0b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -141,7 +142,9 @@
* dispatch. This is only used to determine what callback a route should be assigned to (added,
* removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}.
*/
- private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>();
+ private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>();
+
+ private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>();
/**
* Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be
@@ -282,6 +285,8 @@
MediaRouter2 instance = sAppToProxyRouterMap.get(key);
if (instance == null) {
instance = new MediaRouter2(context, looper, clientPackageName, user);
+ // Register proxy router after instantiation to avoid race condition.
+ ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter();
sAppToProxyRouterMap.put(key, instance);
}
return instance;
@@ -368,6 +373,7 @@
new SystemRoutingController(
ProxyMediaRouter2Impl.getSystemSessionInfoImpl(
mMediaRouterService, clientPackageName));
+
mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user);
}
@@ -883,7 +889,7 @@
newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet());
for (MediaRoute2Info route : newRoutes) {
- MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId());
+ MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId());
if (prevRoute == null) {
addedRoutes.add(route);
} else if (!prevRoute.equals(route)) {
@@ -891,21 +897,21 @@
}
}
- for (int i = 0; i < mPreviousRoutes.size(); i++) {
- if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) {
- removedRoutes.add(mPreviousRoutes.valueAt(i));
+ for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) {
+ if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) {
+ removedRoutes.add(mPreviousFilteredRoutes.valueAt(i));
}
}
// update previous routes
for (MediaRoute2Info route : removedRoutes) {
- mPreviousRoutes.remove(route.getId());
+ mPreviousFilteredRoutes.remove(route.getId());
}
for (MediaRoute2Info route : addedRoutes) {
- mPreviousRoutes.put(route.getId(), route);
+ mPreviousFilteredRoutes.put(route.getId(), route);
}
for (MediaRoute2Info route : changedRoutes) {
- mPreviousRoutes.put(route.getId(), route);
+ mPreviousFilteredRoutes.put(route.getId(), route);
}
if (!addedRoutes.isEmpty()) {
@@ -924,6 +930,27 @@
}
}
+ void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) {
+ List<RoutingController> controllers = getControllers();
+ for (RoutingController controller : controllers) {
+
+ for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) {
+ if (routesMap.containsKey(selectedRoute)
+ && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) {
+ MediaRoute2Info currentRoute = routesMap.get(selectedRoute);
+ MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute);
+ if (!currentRoute.equals(oldRoute)) {
+ notifyControllerUpdated(controller);
+ break;
+ }
+ }
+ }
+ }
+
+ mPreviousUnfilteredRoutes.clear();
+ mPreviousUnfilteredRoutes.putAll(routesMap);
+ }
+
void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) {
synchronized (mLock) {
mRoutes.clear();
@@ -945,6 +972,11 @@
MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler,
this,
mFilteredRoutes));
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler,
+ this,
+ new HashMap<>(mRoutes)));
}
/**
@@ -2153,18 +2185,19 @@
mClientUser = user;
mClientPackageName = clientPackageName;
mClient = new Client();
+ mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
+ }
+ public void registerProxyRouter() {
try {
mMediaRouterService.registerProxyRouter(
mClient,
- context.getApplicationContext().getPackageName(),
- clientPackageName,
- user);
+ mContext.getApplicationContext().getPackageName(),
+ mClientPackageName,
+ mClientUser);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
-
- mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
}
@Override
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index a7ec6c6..8ce1b6d 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -37,31 +37,41 @@
const String EXTRA_PACKAGE_REUSING_GRANTED_CONSENT =
"extra_media_projection_package_reusing_consent";
+ /**
+ * Returns whether a combination of process UID and package has the projection permission.
+ *
+ * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
+ */
@UnsupportedAppUsage
- boolean hasProjectionPermission(int uid, String packageName);
+ boolean hasProjectionPermission(int processUid, String packageName);
/**
* Returns a new {@link IMediaProjection} instance associated with the given package.
+ *
+ * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
*/
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- IMediaProjection createProjection(int uid, String packageName, int type,
+ IMediaProjection createProjection(int processUid, String packageName, int type,
boolean permanentGrant);
/**
* Returns the current {@link IMediaProjection} instance associated with the given
- * package, or {@code null} if it is not possible to re-use the current projection.
+ * package and process UID, or {@code null} if it is not possible to re-use the current
+ * projection.
*
* <p>Should only be invoked when the user has reviewed consent for a re-used projection token.
* Requires that there is a prior session waiting for the user to review consent, and the given
* package details match those on the current projection.
*
* @see {@link #isCurrentProjection}
+ *
+ * @param processUid the process UID as returned by {@link android.os.Process.myUid()}.
*/
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- IMediaProjection getProjection(int uid, String packageName);
+ IMediaProjection getProjection(int processUid, String packageName);
/**
* Returns {@code true} if the given {@link IMediaProjection} corresponds to the current
@@ -162,8 +172,8 @@
*
* <p>Only used for emitting atoms.
*
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
+ * @param hostProcessUid The uid of the process requesting consent to capture, may be an
+ * app or SystemUI.
* @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
* Indicates the entry point for requesting the permission. Must be
* a valid state defined
@@ -172,49 +182,49 @@
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource);
+ oneway void notifyPermissionRequestInitiated(int hostProcessUid, int sessionCreationSource);
/**
* Notifies system server that the permission request was displayed.
*
* <p>Only used for emitting atoms.
*
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
+ * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
*/
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyPermissionRequestDisplayed(int hostUid);
+ oneway void notifyPermissionRequestDisplayed(int hostProcessUid);
/**
* Notifies system server that the permission request was cancelled.
*
* <p>Only used for emitting atoms.
*
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
+ * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
*/
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyPermissionRequestCancelled(int hostUid);
+ oneway void notifyPermissionRequestCancelled(int hostProcessUid);
/**
* Notifies system server that the app selector was displayed.
*
* <p>Only used for emitting atoms.
*
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
+ * @param hostProcessUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
*/
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyAppSelectorDisplayed(int hostUid);
+ oneway void notifyAppSelectorDisplayed(int hostProcessUid);
@EnforcePermission("MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
+ void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode);
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index a9974dc..514ad669 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -39,6 +39,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -68,6 +71,7 @@
) {
val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
Button(
+ modifier = Modifier.semantics { role = Role.DropdownList },
onClick = { expanded = true },
colors = ButtonDefaults.buttonColors(
containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer,
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 21263a9..f7b1a26 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -10,6 +10,13 @@
}
flag {
+ name: "floating_menu_drag_to_hide"
+ namespace: "accessibility"
+ description: "Allows users to hide the FAB then use notification to dismiss or bring it back."
+ bug: "298718415"
+}
+
+flag {
name: "floating_menu_ime_displacement_animation"
namespace: "accessibility"
description: "Adds an animation for when the FAB is displaced by an IME becoming visible."
@@ -28,4 +35,4 @@
namespace: "accessibility"
description: "Animates the floating menu's transition between curved and jagged edges."
bug: "281140482"
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
index 0acc76f..105f572 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PunchHole.kt
@@ -20,24 +20,26 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
-import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.drawscope.translate
-import androidx.compose.ui.graphics.withSaveLayer
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.toSize
@@ -88,11 +90,22 @@
var size: () -> Size,
var offset: () -> Offset,
var shape: () -> Shape,
-) : Modifier.Node(), DrawModifierNode {
+) : Modifier.Node(), DrawModifierNode, LayoutModifierNode {
private var lastSize: Size = Size.Unspecified
private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr
private var lastOutline: Outline? = null
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ return measurable.measure(constraints).run {
+ layout(width, height) {
+ placeWithLayer(0, 0) { compositingStrategy = CompositingStrategy.Offscreen }
+ }
+ }
+ }
+
override fun ContentDrawScope.draw() {
val holeSize = size()
if (holeSize == Size.Zero) {
@@ -100,14 +113,10 @@
return
}
- drawIntoCanvas { canvas ->
- canvas.withSaveLayer(size.toRect(), Paint()) {
- drawContent()
+ drawContent()
- val offset = offset()
- translate(offset.x, offset.y) { drawHole(holeSize) }
- }
- }
+ val offset = offset()
+ translate(offset.x, offset.y) { drawHole(holeSize) }
}
private fun DrawScope.drawHole(size: Size) {
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 c055919..64388b7 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
@@ -315,10 +315,13 @@
with(layoutImpl.state) { coroutineScope.onChangeScene(targetScene.key) }
}
- animateOffset(
+ swipeTransition.animateOffset(
+ coroutineScope = coroutineScope,
initialVelocity = velocity,
targetOffset = targetOffset,
- targetScene = targetScene.key
+ onAnimationCompleted = {
+ layoutState.finishTransition(swipeTransition, idleScene = targetScene.key)
+ }
)
}
@@ -410,34 +413,6 @@
}
}
- private fun animateOffset(
- initialVelocity: Float,
- targetOffset: Float,
- targetScene: SceneKey,
- ) {
- swipeTransition.startOffsetAnimation {
- coroutineScope.launch {
- if (!swipeTransition.isAnimatingOffset) {
- swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset)
- }
- swipeTransition.isAnimatingOffset = true
-
- swipeTransition.offsetAnimatable.animateTo(
- targetOffset,
- // TODO(b/290184746): Make this spring spec configurable.
- spring(
- stiffness = Spring.StiffnessMediumLow,
- visibilityThreshold = OffsetVisibilityThreshold
- ),
- initialVelocity = initialVelocity,
- )
-
- swipeTransition.finishOffsetAnimation()
- layoutState.finishTransition(swipeTransition, targetScene)
- }
- }
- }
-
internal class SwipeTransition(
val _fromScene: Scene,
val _toScene: Scene,
@@ -479,12 +454,14 @@
private var offsetAnimationJob: Job? = null
/** Ends any previous [offsetAnimationJob] and runs the new [job]. */
- fun startOffsetAnimation(job: () -> Job) {
+ private fun startOffsetAnimation(job: () -> Job) {
cancelOffsetAnimation()
offsetAnimationJob = job()
}
/** Cancel any ongoing offset animation. */
+ // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
+ // the same time.
fun cancelOffsetAnimation() {
offsetAnimationJob?.cancel()
finishOffsetAnimation()
@@ -496,6 +473,43 @@
dragOffset = offsetAnimatable.value
}
}
+
+ // TODO(b/290184746): Make this spring spec configurable.
+ private val animationSpec =
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = OffsetVisibilityThreshold
+ )
+
+ fun animateOffset(
+ // TODO(b/317063114) The CoroutineScope should be removed.
+ coroutineScope: CoroutineScope,
+ initialVelocity: Float,
+ targetOffset: Float,
+ onAnimationCompleted: () -> Unit,
+ ) {
+ startOffsetAnimation {
+ coroutineScope.launch {
+ animateOffset(targetOffset, initialVelocity)
+ onAnimationCompleted()
+ }
+ }
+ }
+
+ private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) {
+ if (!isAnimatingOffset) {
+ offsetAnimatable.snapTo(dragOffset)
+ }
+ isAnimatingOffset = true
+
+ offsetAnimatable.animateTo(
+ targetValue = targetOffset,
+ animationSpec = animationSpec,
+ initialVelocity = initialVelocity,
+ )
+
+ finishOffsetAnimation()
+ }
}
companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 41bde52..3dfe65a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -22,6 +22,7 @@
import android.os.UserHandle
import android.provider.Settings
import androidx.annotation.OpenForTesting
+import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.log.core.Logger
@@ -120,8 +121,9 @@
override fun onPluginAttached(
manager: PluginLifecycleManager<ClockProviderPlugin>
): Boolean {
- manager.isDebug = !keepAllLoaded
-
+ manager.setLogFunc({ tag, msg ->
+ (clockBuffers?.infraMessageBuffer as LogBuffer?)?.log(tag, LogLevel.DEBUG, msg)
+ })
if (keepAllLoaded) {
// Always load new plugins if requested
return true
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
new file mode 100644
index 0000000..721fc49
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 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.communal.log
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
+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.any
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalLoggerStartableTest : SysuiTestCase() {
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+
+ private lateinit var testScope: TestScope
+ private lateinit var communalInteractor: CommunalInteractor
+ private lateinit var underTest: CommunalLoggerStartable
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val withDeps = CommunalInteractorFactory.create()
+ testScope = withDeps.testScope
+ communalInteractor = withDeps.communalInteractor
+
+ underTest =
+ CommunalLoggerStartable(
+ testScope.backgroundScope,
+ communalInteractor,
+ uiEventLogger,
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun transitionStateLogging_enterCommunalHub() =
+ testScope.runTest {
+ // Transition state is default (non-communal)
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+ communalInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // Verify nothing is logged from the default state
+ verify(uiEventLogger, never()).log(any())
+
+ // Start transition to communal
+ transitionState.value = transition(to = CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
+
+ // Finish transition to communal
+ transitionState.value = idle(CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH)
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+ }
+
+ @Test
+ fun transitionStateLogging_enterCommunalHub_canceled() =
+ testScope.runTest {
+ // Transition state is default (non-communal)
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.DEFAULT))
+ communalInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // Verify nothing is logged from the default state
+ verify(uiEventLogger, never()).log(any())
+
+ // Start transition to communal
+ transitionState.value = transition(to = CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START)
+
+ // Cancel the transition
+ transitionState.value = idle(CommunalSceneKey.DEFAULT)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL)
+
+ // Verify neither SHOWN nor GONE is logged
+ verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+ verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+ }
+
+ @Test
+ fun transitionStateLogging_exitCommunalHub() =
+ testScope.runTest {
+ // Transition state is communal
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+ communalInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // Verify SHOWN is logged when it's the default state
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+
+ // Start transition from communal
+ transitionState.value = transition(from = CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
+
+ // Finish transition to communal
+ transitionState.value = idle(CommunalSceneKey.DEFAULT)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH)
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+ }
+
+ @Test
+ fun transitionStateLogging_exitCommunalHub_canceled() =
+ testScope.runTest {
+ // Transition state is communal
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(idle(CommunalSceneKey.Communal))
+ communalInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // Clear the initial SHOWN event from the logger
+ clearInvocations(uiEventLogger)
+
+ // Start transition from communal
+ transitionState.value = transition(from = CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START)
+
+ // Cancel the transition
+ transitionState.value = idle(CommunalSceneKey.Communal)
+ runCurrent()
+
+ // Verify UiEvent logged
+ verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL)
+
+ // Verify neither SHOWN nor GONE is logged
+ verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+ verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+ }
+
+ private fun transition(
+ from: CommunalSceneKey = CommunalSceneKey.DEFAULT,
+ to: CommunalSceneKey = CommunalSceneKey.DEFAULT,
+ ): ObservableCommunalTransitionState.Transition {
+ return ObservableCommunalTransitionState.Transition(
+ fromScene = from,
+ toScene = to,
+ progress = emptyFlow(),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = emptyFlow(),
+ )
+ }
+
+ private fun idle(sceneKey: CommunalSceneKey): ObservableCommunalTransitionState.Idle {
+ return ObservableCommunalTransitionState.Idle(sceneKey)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
index e7037a6..9daf186 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
@@ -94,7 +94,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(5)
+ assertThat(values.size).isEqualTo(6)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index e141c2b..f1690daf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -95,7 +95,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(6)
+ assertThat(values.size).isEqualTo(7)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
}
@@ -117,7 +117,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(3)
+ assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
@@ -232,7 +232,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 897ce6d..f763a67 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -59,9 +59,9 @@
testScope,
)
- // Only three values should be present, since the dream overlay runs for a small
+ // Only five values should be present, since the dream overlay runs for a small
// fraction of the overall animation time
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
@@ -84,7 +84,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 4843f8b..74025fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -73,9 +73,9 @@
testScope = testScope,
)
- // Only three values should be present, since the dream overlay runs for a small
+ // Only five values should be present, since the dream overlay runs for a small
// fraction of the overall animation time
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
@@ -98,10 +98,10 @@
testScope = testScope,
)
- assertThat(values.size).isEqualTo(5)
+ assertThat(values.size).isEqualTo(6)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
// Validate finished value
- assertThat(values[4]).isEqualTo(0f)
+ assertThat(values[5]).isEqualTo(0f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index a1b8aab..6fcb0c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -74,9 +74,9 @@
),
testScope = testScope,
)
- // Only 3 values should be present, since the dream overlay runs for a small fraction
+ // Only 5 values should be present, since the dream overlay runs for a small fraction
// of the overall animation time
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 2111ad5..639114c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -33,6 +33,7 @@
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,6 +55,7 @@
fun lockscreenFadeIn() =
testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
listOf(
@@ -83,6 +85,7 @@
100
)
val values by collectValues(underTest.lockscreenTranslationY)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
listOf(
@@ -95,7 +98,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(4)
+ assertThat(values.size).isEqualTo(5)
values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
}
@@ -107,6 +110,7 @@
100
)
val values by collectValues(underTest.lockscreenTranslationY)
+ runCurrent()
keyguardTransitionRepository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
@@ -117,6 +121,7 @@
fun deviceEntryParentViewFadeIn() =
testScope.runTest {
val values by collectValues(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
listOf(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 90b8362..30b87bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -65,6 +66,7 @@
fun bouncerAlpha() =
testScope.runTest {
val values by collectValues(underTest.bouncerAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
listOf(
@@ -83,6 +85,7 @@
fun bouncerAlpha_runDimissFromKeyguard() =
testScope.runTest {
val values by collectValues(underTest.bouncerAlpha)
+ runCurrent()
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
@@ -95,7 +98,7 @@
testScope,
)
- assertThat(values.size).isEqualTo(1)
+ assertThat(values.size).isEqualTo(2)
values.forEach { assertThat(it).isEqualTo(0f) }
}
@@ -103,11 +106,12 @@
fun lockscreenAlpha() =
testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
keyguardTransitionRepository.sendTransitionStep(step(1f))
- assertThat(values.size).isEqualTo(1)
+ assertThat(values.size).isEqualTo(2)
values.forEach { assertThat(it).isEqualTo(0f) }
}
@@ -115,13 +119,14 @@
fun lockscreenAlpha_runDimissFromKeyguard() =
testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
+ runCurrent()
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
keyguardTransitionRepository.sendTransitionStep(step(1f))
- assertThat(values.size).isEqualTo(1)
+ assertThat(values.size).isEqualTo(2)
values.forEach { assertThat(it).isEqualTo(1f) }
}
@@ -129,13 +134,14 @@
fun lockscreenAlpha_leaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
+ runCurrent()
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
keyguardTransitionRepository.sendTransitionStep(step(1f))
- assertThat(values.size).isEqualTo(1)
+ assertThat(values.size).isEqualTo(2)
values.forEach { assertThat(it).isEqualTo(1f) }
}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
index 3e5e8a0..f0ce460 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
@@ -18,6 +18,8 @@
import android.content.ComponentName;
+import java.util.function.BiConsumer;
+
/**
* Provides the ability for consumers to control plugin lifecycle.
*
@@ -33,11 +35,8 @@
/** Returns the currently loaded plugin instance (if plugin is loaded) */
T getPlugin();
- /** Returns true if the lifecycle manager should log debug messages */
- boolean getIsDebug();
-
- /** Sets whether or not hte lifecycle manager should log debug messages */
- void setIsDebug(boolean debug);
+ /** Log tag and messages will be sent to the provided Consumer */
+ void setLogFunc(BiConsumer<String, String> logConsumer);
/** returns true if the plugin is currently loaded */
default boolean isLoaded() {
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml
new file mode 100644
index 0000000..2161a62
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_branded_vpn.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** 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.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml
new file mode 100644
index 0000000..2161a62
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_no_internet_vpn_ic.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** 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.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h4V9H12.09zM18,13hv3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H18V13z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M22,10h-2v8h2V10z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5e10905..f55a11e2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -449,11 +449,11 @@
<!-- Content description after successful auth when confirmation required -->
<string name="fingerprint_dialog_authenticated_confirmation">Press the unlock icon to continue</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
- <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead">Face not recognized. Use fingerprint instead.</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
<string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string>
<!-- Message shown to inform the user a face cannot be recognized. [CHAR LIMIT=25] -->
- <string name="keyguard_face_failed">Can\u2019t recognize face</string>
+ <string name="keyguard_face_failed">Face not recognized</string>
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
<!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=59] -->
@@ -2284,6 +2284,8 @@
<string name="notification_channel_storage">Storage</string>
<!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] -->
<string name="notification_channel_hints">Hints</string>
+ <!-- Title for the notification channel for accessibility related (i.e. accessibility floating menu). [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_accessibility">Accessibility</string>
<!-- App label of the instant apps notification [CHAR LIMIT=60] -->
<string name="instant_apps">Instant Apps</string>
@@ -2554,6 +2556,11 @@
<string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string>
<!-- Text for the undo action button of the message view of the accessibility floating menu to perform undo operation. [CHAR LIMIT=30]-->
<string name="accessibility_floating_button_undo">Undo</string>
+ <!-- Notification title shown when accessibility floating button is in hidden state. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_floating_button_hidden_notification_title">Accessibility button hidden</string>
+ <!-- Notification content text to explain user can tap notification to bring back accessibility floating button. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_floating_button_hidden_notification_text">Tap to show accessibility button</string>
+
<!-- Text for the message view with undo action of the accessibility floating menu to show which feature shortcut was removed. [CHAR LIMIT=30]-->
<string name="accessibility_floating_button_undo_message_label_text"><xliff:g id="feature name" example="Magnification">%s</xliff:g> shortcut removed</string>
<!-- Text for the message view with undo action of the accessibility floating menu to show how many features shortcuts were removed. [CHAR LIMIT=30]-->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 387f2e1..87cc86f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -38,6 +38,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
@@ -57,7 +58,7 @@
private final PluginFactory<T> mPluginFactory;
private final String mTag;
- private boolean mIsDebug = false;
+ private BiConsumer<String, String> mLogConsumer = null;
private Context mPluginContext;
private T mPlugin;
@@ -86,17 +87,13 @@
return mTag;
}
- public boolean getIsDebug() {
- return mIsDebug;
+ public void setLogFunc(BiConsumer logConsumer) {
+ mLogConsumer = logConsumer;
}
- public void setIsDebug(boolean debug) {
- mIsDebug = debug;
- }
-
- private void logDebug(String message) {
- if (mIsDebug) {
- Log.i(mTag, message);
+ private void log(String message) {
+ if (mLogConsumer != null) {
+ mLogConsumer.accept(mTag, message);
}
}
@@ -105,19 +102,19 @@
boolean loadPlugin = mListener.onPluginAttached(this);
if (!loadPlugin) {
if (mPlugin != null) {
- logDebug("onCreate: auto-unload");
+ log("onCreate: auto-unload");
unloadPlugin();
}
return;
}
if (mPlugin == null) {
- logDebug("onCreate auto-load");
+ log("onCreate auto-load");
loadPlugin();
return;
}
- logDebug("onCreate: load callbacks");
+ log("onCreate: load callbacks");
mPluginFactory.checkVersion(mPlugin);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onCreate for plugins that aren't fragments, as fragments
@@ -129,7 +126,7 @@
/** Alerts listener and plugin that the plugin is being shutdown. */
public synchronized void onDestroy() {
- logDebug("onDestroy");
+ log("onDestroy");
unloadPlugin();
mListener.onPluginDetached(this);
}
@@ -145,7 +142,7 @@
*/
public synchronized void loadPlugin() {
if (mPlugin != null) {
- logDebug("Load request when already loaded");
+ log("Load request when already loaded");
return;
}
@@ -157,7 +154,7 @@
return;
}
- logDebug("Loaded plugin; running callbacks");
+ log("Loaded plugin; running callbacks");
mPluginFactory.checkVersion(mPlugin);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onCreate for plugins that aren't fragments, as fragments
@@ -174,11 +171,11 @@
*/
public synchronized void unloadPlugin() {
if (mPlugin == null) {
- logDebug("Unload request when already unloaded");
+ log("Unload request when already unloaded");
return;
}
- logDebug("Unloading plugin, running callbacks");
+ log("Unloading plugin, running callbacks");
mListener.onPluginUnloaded(mPlugin, this);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onDestroy for plugins that aren't fragments, as fragments
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 823bd2d..7088829 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -88,18 +88,23 @@
*/
void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
- /**
- * Sent when split keyboard shortcut is triggered to enter stage split.
- */
- void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
+ /**
+ * Sent when when navigation bar luma sampling is enabled or disabled.
+ */
+ void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) = 23;
- /**
- * Sent when the surface for navigation bar is created or changed
- */
- void onNavigationBarSurface(in SurfaceControl surface) = 26;
+ /**
+ * Sent when split keyboard shortcut is triggered to enter stage split.
+ */
+ void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
- /**
- * Sent when the task bar stash state is toggled.
- */
- void onTaskbarToggled() = 27;
+ /**
+ * Sent when the surface for navigation bar is created or changed
+ */
+ void onNavigationBarSurface(in SurfaceControl surface) = 26;
+
+ /**
+ * Sent when the task bar stash state is toggled.
+ */
+ void onTaskbarToggled() = 27;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index 49e0df6..568b24d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -116,6 +117,11 @@
mMagnetizedObject.setMagnetListener(magnetListener);
}
+ @VisibleForTesting
+ MagnetizedObject.MagnetListener getMagnetListener() {
+ return mMagnetizedObject.getMagnetListener();
+ }
+
void maybeConsumeDownMotionEvent(MotionEvent event) {
mMagnetizedObject.maybeConsumeMotionEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java
new file mode 100644
index 0000000..b5eeaa1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactory.java
@@ -0,0 +1,75 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.systemui.res.R;
+import com.android.systemui.util.NotificationChannels;
+
+class MenuNotificationFactory {
+ public static final String ACTION_UNDO =
+ "com.android.systemui.accessibility.floatingmenu.action.UNDO";
+ public static final String ACTION_DELETE =
+ "com.android.systemui.accessibility.floatingmenu.action.DELETE";
+
+ private final Context mContext;
+
+ MenuNotificationFactory(Context context) {
+ mContext = context;
+ }
+
+ public Notification createHiddenNotification() {
+ final CharSequence title = mContext.getText(
+ R.string.accessibility_floating_button_hidden_notification_title);
+ final CharSequence content = mContext.getText(
+ R.string.accessibility_floating_button_hidden_notification_text);
+
+ return new Notification.Builder(mContext, NotificationChannels.ALERTS)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setSmallIcon(R.drawable.ic_settings_24dp)
+ .setContentIntent(buildUndoIntent())
+ .setDeleteIntent(buildDeleteIntent())
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setLocalOnly(true)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .build();
+ }
+
+ private PendingIntent buildUndoIntent() {
+ final Intent intent = new Intent(ACTION_UNDO);
+
+ return PendingIntent.getBroadcast(mContext, /* requestCode= */ 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+
+ }
+
+ private PendingIntent buildDeleteIntent() {
+ final Intent intent = new Intent(ACTION_DELETE);
+
+ return PendingIntent.getBroadcastAsUser(mContext, /* requestCode= */ 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 62d5feb..6869bba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -26,16 +26,21 @@
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState;
import static com.android.systemui.accessibility.floatingmenu.MenuMessageView.Index;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -58,6 +63,7 @@
import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.util.Preconditions;
import com.android.systemui.Flags;
import com.android.systemui.res.R;
@@ -91,6 +97,8 @@
private final MenuViewAppearance mMenuViewAppearance;
private final MenuAnimationController mMenuAnimationController;
private final AccessibilityManager mAccessibilityManager;
+ private final NotificationManager mNotificationManager;
+ private final MenuNotificationFactory mNotificationFactory;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final IAccessibilityFloatingMenu mFloatingMenu;
private final SecureSettings mSecureSettings;
@@ -103,7 +111,9 @@
private final Rect mImeInsetsRect = new Rect();
private boolean mIsMigrationTooltipShowing;
private boolean mShouldShowDockTooltip;
+ private boolean mIsNotificationShown;
private Optional<MenuEduTooltipView> mEduTooltipView = Optional.empty();
+ private BroadcastReceiver mNotificationActionReceiver;
@IntDef({
LayerIndex.MENU_VIEW,
@@ -184,10 +194,16 @@
mMenuViewAppearance = new MenuViewAppearance(context, windowManager);
mMenuView = new MenuView(context, mMenuViewModel, mMenuViewAppearance);
mMenuAnimationController = mMenuView.getMenuAnimationController();
- mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
+ if (Flags.floatingMenuDragToHide()) {
+ mMenuAnimationController.setDismissCallback(this::hideMenuAndShowNotification);
+ } else {
+ mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage);
+ }
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
DismissViewUtils.setup(mDismissView);
+ mNotificationFactory = new MenuNotificationFactory(context);
+ mNotificationManager = context.getSystemService(NotificationManager.class);
mDragToInteractAnimationController = new DragToInteractAnimationController(
mDismissView, mMenuView);
mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
@@ -204,7 +220,11 @@
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- hideMenuAndShowMessage();
+ if (Flags.floatingMenuDragToHide()) {
+ hideMenuAndShowNotification();
+ } else {
+ hideMenuAndShowMessage();
+ }
mDismissView.hide();
mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
}
@@ -218,18 +238,25 @@
mMessageView = new MenuMessageView(context);
mMenuView.setOnTargetFeaturesChangeListener(newTargetFeatures -> {
- if (newTargetFeatures.size() < 1) {
- return;
- }
-
- // During the undo action period, the pending action will be canceled and undo back
- // to the previous state if users did any action related to the accessibility features.
- if (mMessageView.getVisibility() == VISIBLE) {
+ if (Flags.floatingMenuDragToHide()) {
+ dismissNotification();
undo();
- }
+ } else {
+ if (newTargetFeatures.size() < 1) {
+ return;
+ }
- final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW);
- messageText.setText(getMessageText(newTargetFeatures));
+ // During the undo action period, the pending action will be canceled and undo back
+ // to the previous state if users did any action related to the accessibility
+ // features.
+ if (mMessageView.getVisibility() == VISIBLE) {
+ undo();
+ }
+
+
+ final TextView messageText = (TextView) mMessageView.getChildAt(Index.TEXT_VIEW);
+ messageText.setText(getMessageText(newTargetFeatures));
+ }
});
addView(mMenuView, LayerIndex.MENU_VIEW);
@@ -456,6 +483,50 @@
mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
}
+ private void hideMenuAndShowNotification() {
+ mMenuAnimationController.startShrinkAnimation(() -> mMenuView.setVisibility(GONE));
+ showNotification();
+ }
+
+ private void showNotification() {
+ registerReceiverIfNeeded();
+ if (!mIsNotificationShown) {
+ mNotificationManager.notify(
+ SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN,
+ mNotificationFactory.createHiddenNotification());
+ mIsNotificationShown = true;
+ }
+ }
+
+ private void dismissNotification() {
+ unregisterReceiverIfNeeded();
+ if (mIsNotificationShown) {
+ mNotificationManager.cancel(
+ SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+ mIsNotificationShown = false;
+ }
+ }
+
+ private void registerReceiverIfNeeded() {
+ if (mNotificationActionReceiver != null) {
+ return;
+ }
+ mNotificationActionReceiver = new MenuNotificationActionReceiver();
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_UNDO);
+ intentFilter.addAction(ACTION_DELETE);
+ getContext().registerReceiver(mNotificationActionReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED);
+ }
+
+ private void unregisterReceiverIfNeeded() {
+ if (mNotificationActionReceiver == null) {
+ return;
+ }
+ getContext().unregisterReceiver(mNotificationActionReceiver);
+ mNotificationActionReceiver = null;
+ }
+
private void undo() {
mHandler.removeCallbacksAndMessages(/* token= */ null);
mMessageView.setVisibility(GONE);
@@ -464,4 +535,23 @@
mMenuView.setVisibility(VISIBLE);
mMenuAnimationController.startGrowAnimation();
}
+
+ @VisibleForTesting
+ DragToInteractAnimationController getDragToInteractAnimationController() {
+ return mDragToInteractAnimationController;
+ }
+
+ private class MenuNotificationActionReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_UNDO.equals(action)) {
+ dismissNotification();
+ undo();
+ } else if (ACTION_DELETE.equals(action)) {
+ dismissNotification();
+ mDismissMenuAction.run();
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
new file mode 100644
index 0000000..889023e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 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.communal.log
+
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.pairwise
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+/** A [CoreStartable] responsible for logging metrics for the communal hub. */
+@SysUISingleton
+class CommunalLoggerStartable
+@Inject
+constructor(
+ @Background private val backgroundScope: CoroutineScope,
+ private val communalInteractor: CommunalInteractor,
+ private val uiEventLogger: UiEventLogger,
+) : CoreStartable {
+
+ override fun start() {
+ communalInteractor.transitionState
+ .map { state ->
+ when {
+ state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN
+ state.isNotOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_GONE
+ else -> null
+ }
+ }
+ .filterNotNull()
+ .distinctUntilChanged()
+ // Drop the default value.
+ .drop(1)
+ .onEach { uiEvent -> uiEventLogger.log(uiEvent) }
+ .launchIn(backgroundScope)
+
+ communalInteractor.transitionState
+ .pairwise()
+ .map { (old, new) ->
+ when {
+ new.isOnCommunal() && old.isSwipingToCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH
+ new.isOnCommunal() && old.isSwipingFromCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL
+ new.isNotOnCommunal() && old.isSwipingFromCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH
+ new.isNotOnCommunal() && old.isSwipingToCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL
+ new.isSwipingToCommunal() && old.isNotOnCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START
+ new.isSwipingFromCommunal() && old.isOnCommunal() ->
+ CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START
+ else -> null
+ }
+ }
+ .filterNotNull()
+ .distinctUntilChanged()
+ .onEach { uiEvent -> uiEventLogger.log(uiEvent) }
+ .launchIn(backgroundScope)
+ }
+}
+
+/** Whether currently in communal scene. */
+private fun ObservableCommunalTransitionState.isOnCommunal(): Boolean {
+ return this is ObservableCommunalTransitionState.Idle && scene == CommunalSceneKey.Communal
+}
+
+/** Whether currently in a scene other than communal. */
+private fun ObservableCommunalTransitionState.isNotOnCommunal(): Boolean {
+ return this is ObservableCommunalTransitionState.Idle && scene != CommunalSceneKey.Communal
+}
+
+/** Whether currently transitioning from another scene to communal. */
+private fun ObservableCommunalTransitionState.isSwipingToCommunal(): Boolean {
+ return this is ObservableCommunalTransitionState.Transition &&
+ toScene == CommunalSceneKey.Communal &&
+ isInitiatedByUserInput
+}
+
+/** Whether currently transitioning from communal to another scene. */
+private fun ObservableCommunalTransitionState.isSwipingFromCommunal(): Boolean {
+ return this is ObservableCommunalTransitionState.Transition &&
+ fromScene == CommunalSceneKey.Communal &&
+ isInitiatedByUserInput
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
index e167f3e..b64c195 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
@@ -22,8 +22,6 @@
/** UI events for the Communal Hub. */
enum class CommunalUiEvent(private val id: Int) : UiEventEnum {
@UiEvent(doc = "Communal Hub is fully shown") COMMUNAL_HUB_SHOWN(1566),
- @UiEvent(doc = "Communal Hub starts entering") COMMUNAL_HUB_ENTERING(1575),
- @UiEvent(doc = "Communal Hub starts exiting") COMMUNAL_HUB_EXITING(1576),
@UiEvent(doc = "Communal Hub is fully gone") COMMUNAL_HUB_GONE(1577),
@UiEvent(doc = "Communal Hub times out") COMMUNAL_HUB_TIMEOUT(1578),
@UiEvent(doc = "The visible content in the Communal Hub is fully loaded and rendered")
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index bfc6f2b..380ed61 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -30,6 +30,8 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
import javax.inject.Inject
@@ -41,6 +43,7 @@
constructor(
private val communalViewModel: CommunalEditModeViewModel,
private var windowManagerService: IWindowManager? = null,
+ private val uiEventLogger: UiEventLogger,
) : ComponentActivity() {
companion object {
private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
@@ -54,6 +57,8 @@
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
RESULT_OK -> {
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN)
+
result.data?.let { intent ->
val isPendingWidgetDrag =
intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
@@ -144,4 +149,16 @@
communalViewModel.setConfigurationResult(resultCode)
}
}
+
+ override fun onStart() {
+ super.onStart()
+
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
+ }
+
+ override fun onStop() {
+ super.onStop()
+
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_GONE)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 87a736d..8d82b55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -24,6 +24,7 @@
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
+import com.android.systemui.communal.log.CommunalLoggerStartable
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
import com.android.systemui.dreams.AssistantAttentionMonitor
@@ -318,4 +319,9 @@
@IntoMap
@ClassKey(KeyguardDismissBinder::class)
abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(CommunalLoggerStartable::class)
+ abstract fun bindCommunalLoggerStartable(impl: CommunalLoggerStartable): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index f7d1543..5b0c562 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -154,6 +154,8 @@
val dozingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DOZING, LOCKSCREEN)
+ val transitions = repository.transitions
+
/** Receive all [TransitionStep] matching a filter of [from]->[to] */
fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> {
return repository.transition(from, to)
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 cf1d247..4abda74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -17,9 +17,13 @@
import android.view.animation.Interpolator
import com.android.app.animation.Interpolators.LINEAR
+import com.android.app.tracing.coroutines.launch
import com.android.keyguard.logging.KeyguardTransitionAnimationLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
@@ -31,10 +35,12 @@
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
/**
* Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and
@@ -45,21 +51,49 @@
@Inject
constructor(
@Application private val scope: CoroutineScope,
+ private val transitionInteractor: KeyguardTransitionInteractor,
private val logger: KeyguardTransitionAnimationLogger,
) {
+ private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
- /**
- * Invoke once per transition between FROM->TO states to get access to
- * [SharedFlowBuilder#sharedFlow].
- */
+ init {
+ scope.launch("KeyguardTransitionAnimationFlow") {
+ transitionInteractor.transitions.collect {
+ // FROM->TO
+ transitionMap[Edge(it.from, it.to)]?.emit(it)
+ // FROM->(ANY)
+ transitionMap[Edge(it.from, null)]?.emit(it)
+ // (ANY)->TO
+ transitionMap[Edge(null, it.to)]?.emit(it)
+ }
+ }
+ }
+
+ private fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
+ return transitionMap.getOrPut(edge) {
+ MutableSharedFlow<TransitionStep>(
+ extraBufferCapacity = 10,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ }
+ }
+
+ /** Invoke once per transition between FROM->TO states to get access to a shared flow. */
fun setup(
duration: Duration,
- stepFlow: Flow<TransitionStep>,
- ) = SharedFlowBuilder(duration, stepFlow)
+ from: KeyguardState?,
+ to: KeyguardState?,
+ ): FlowBuilder {
+ if (from == null && to == null) {
+ throw IllegalArgumentException("from and to are both null")
+ }
- inner class SharedFlowBuilder(
+ return FlowBuilder(duration, Edge(from, to))
+ }
+
+ inner class FlowBuilder(
private val transitionDuration: Duration,
- private val stepFlow: Flow<TransitionStep>,
+ private val edge: Edge,
) {
/**
* Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
@@ -115,20 +149,21 @@
}?.let { onStep(interpolator.getInterpolation(it)) }
}
- return stepFlow
+ return getOrCreateFlow(edge)
.map { step ->
- val value =
- when (step.transitionState) {
- STARTED -> stepToValue(step)
- RUNNING -> stepToValue(step)
- CANCELED -> onCancel?.invoke()
- FINISHED -> onFinish?.invoke()
- }
- logger.logTransitionStep(name, step, value)
- value
+ StateToValue(
+ step.transitionState,
+ when (step.transitionState) {
+ STARTED -> stepToValue(step)
+ RUNNING -> stepToValue(step)
+ CANCELED -> onCancel?.invoke()
+ FINISHED -> onFinish?.invoke()
+ }
+ )
+ .also { logger.logTransitionStep(name, step, it.value) }
}
- .filterNotNull()
.distinctUntilChanged()
+ .mapNotNull { stateToValue -> stateToValue.value }
}
/**
@@ -138,4 +173,14 @@
return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
}
}
+
+ data class Edge(
+ val from: KeyguardState?,
+ val to: KeyguardState?,
+ )
+
+ data class StateToValue(
+ val transitionState: TransitionState,
+ val value: Float?,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index a8e3be7..b4b48a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-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
@@ -38,14 +37,14 @@
class AlternateBouncerToAodTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
- stepFlow = interactor.transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD),
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 5d6b0ce..3737e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -37,14 +36,14 @@
class AlternateBouncerToGoneTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
bouncerToGoneFlows: BouncerToGoneFlows,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = TO_GONE_DURATION,
- stepFlow = interactor.transition(ALTERNATE_BOUNCER, KeyguardState.GONE),
+ from = ALTERNATE_BOUNCER,
+ to = KeyguardState.GONE,
)
/** Scrim alpha values */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 8e729f7..2526f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -19,7 +19,6 @@
import android.graphics.Color
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -37,7 +36,6 @@
@Inject
constructor(
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- transitionInteractor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
// When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
@@ -46,7 +44,8 @@
animationFlow
.setup(
duration = TRANSITION_DURATION_MS,
- stepFlow = transitionInteractor.anyStateToAlternateBouncerTransition,
+ from = null,
+ to = ALTERNATE_BOUNCER,
)
.sharedFlow(
duration = TRANSITION_DURATION_MS,
@@ -60,7 +59,8 @@
animationFlow
.setup(
TRANSITION_DURATION_MS,
- transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER),
+ from = ALTERNATE_BOUNCER,
+ to = null,
)
.sharedFlow(
duration = TRANSITION_DURATION_MS,
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
index 2b14521..b92a9a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
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
@@ -31,14 +30,14 @@
class AodToGoneTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_GONE_DURATION,
- stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE),
+ from = KeyguardState.AOD,
+ to = 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 5e552e1..266fd02 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
@@ -19,7 +19,7 @@
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -37,7 +37,6 @@
class AodToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -45,7 +44,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- stepFlow = interactor.aodToLockscreenTransition,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
)
/** Ensure alpha is set to be visible */
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
index d283af3..105a7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -18,7 +18,6 @@
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
@@ -29,13 +28,13 @@
class AodToOccludedTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
- stepFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED),
+ from = KeyguardState.AOD,
+ to = KeyguardState.OCCLUDED,
)
override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 41dc157..924fc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -21,7 +21,6 @@
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -41,7 +40,6 @@
class BouncerToGoneFlows
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
@@ -76,7 +74,8 @@
val transitionAnimation =
animationFlow.setup(
duration = duration,
- stepFlow = interactor.transition(fromState, GONE)
+ from = fromState,
+ to = GONE,
)
return shadeInteractor.shadeExpansion.flatMapLatest { shadeExpansion ->
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
index 0b34326..e4610c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -34,13 +34,13 @@
class DozingToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
- stepFlow = interactor.dozingToLockscreenTransition,
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
index 8bcf3f8..67568e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
class DreamingHostedToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- stepFlow = interactor.dreamingLockscreenHostedToLockscreenTransition,
+ from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ to = KeyguardState.LOCKSCREEN,
)
val shortcutsAlpha: Flow<Float> =
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 5f620af..ead2d48 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
@@ -22,6 +22,7 @@
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.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -52,7 +53,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- stepFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition,
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.LOCKSCREEN,
)
val transitionEnded =
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 3f27eb0..ba04fd3 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
@@ -20,7 +20,7 @@
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -36,7 +36,6 @@
class GoneToAodTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -44,7 +43,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_AOD_DURATION,
- stepFlow = interactor.goneToAodTransition,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
)
/** y-translation from the top of the screen for AOD */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
index bba790a..b527463 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -32,14 +32,14 @@
class GoneToDreamingLockscreenHostedTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_DURATION,
- stepFlow = interactor.goneToDreamingLockscreenHostedTransition,
+ from = KeyguardState.GONE,
+ to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
)
/** Lockscreen views alpha - hide immediately */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 6762ba6..102242a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -19,7 +19,7 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -30,14 +30,14 @@
class GoneToDreamingTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_DURATION,
- stepFlow = interactor.goneToDreamingTransition,
+ from = KeyguardState.GONE,
+ to = KeyguardState.DREAMING,
)
/** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
index adae8ab..793abb4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
class GoneToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- stepFlow = interactor.goneToLockscreenTransition
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN
)
val shortcutsAlpha: Flow<Float> =
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
index 65614f4..7bf51a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -19,7 +19,7 @@
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -36,7 +36,6 @@
class LockscreenToAodTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
shadeDependentFlows: ShadeDependentFlows,
animationFlow: KeyguardTransitionAnimationFlow,
@@ -45,7 +44,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
- stepFlow = interactor.lockscreenToAodTransition,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index accb20c..4c0cd2f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
class LockscreenToDozingTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_DOZING_DURATION,
- stepFlow = interactor.lockscreenToDozingTransition
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
index c649b12..19b9cf47 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -28,14 +28,14 @@
class LockscreenToDreamingHostedTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_HOSTED_DURATION,
- stepFlow = interactor.lockscreenToDreamingLockscreenHostedTransition
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
)
val shortcutsAlpha: Flow<Float> =
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 7f75b54..13522a6 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
@@ -19,7 +19,7 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -34,14 +34,14 @@
class LockscreenToDreamingTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
shadeDependentFlows: ShadeDependentFlows,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_DURATION,
- stepFlow = interactor.lockscreenToDreamingTransition,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
)
/** Lockscreen views y-translation */
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
index 9e19713..a26ef07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -18,7 +18,6 @@
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
@@ -35,14 +34,14 @@
class LockscreenToGoneTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
- stepFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
)
val shortcutsAlpha: Flow<Float> =
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 9db0b77..dd6652e 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
@@ -20,7 +20,7 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
@@ -37,7 +37,6 @@
class LockscreenToOccludedTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
shadeDependentFlows: ShadeDependentFlows,
configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
@@ -46,7 +45,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_OCCLUDED_DURATION,
- stepFlow = interactor.lockscreenToOccludedTransition,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
)
/** Lockscreen views alpha */
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
index 52e3257..ce47f3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -18,7 +18,6 @@
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
@@ -26,7 +25,6 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
/**
* Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -37,21 +35,21 @@
class LockscreenToPrimaryBouncerTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
shadeDependentFlows: ShadeDependentFlows,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- stepFlow =
- interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
)
val shortcutsAlpha: Flow<Float> =
- interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map {
- 1 - it.value
- }
+ transitionAnimation.sharedFlow(
+ duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ onStep = { 1f - it }
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
shadeDependentFlows.transitionFlow(
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
index ed5e83c..07c1141 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
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
@@ -35,14 +34,14 @@
class OccludedToAodTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
- stepFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD),
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.AOD,
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
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 4c24f83..90195bd 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
@@ -21,7 +21,7 @@
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
@@ -41,7 +41,6 @@
class OccludedToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
@@ -50,7 +49,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- stepFlow = interactor.occludedToLockscreenTransition,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
)
/** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
index 93482ea..74094be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -27,14 +27,14 @@
class OffToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
duration = 250.milliseconds,
- stepFlow = interactor.offToLockscreenTransition
+ from = KeyguardState.OFF,
+ to = KeyguardState.LOCKSCREEN,
)
val shortcutsAlpha: Flow<Float> =
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
index b0e2aa2..cd8e2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,6 @@
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
@@ -39,14 +38,14 @@
class PrimaryBouncerToAodTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
- stepFlow = interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD),
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.AOD,
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 9dbe97f..4f28b46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -22,7 +22,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
@@ -44,7 +43,6 @@
class PrimaryBouncerToGoneTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
@@ -55,7 +53,8 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_GONE_DURATION,
- stepFlow = interactor.transition(PRIMARY_BOUNCER, GONE)
+ from = PRIMARY_BOUNCER,
+ to = GONE,
)
private var leaveShadeOpen: Boolean = false
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
index b2eed60..284a134 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -19,7 +19,6 @@
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
@@ -28,7 +27,6 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
/**
* Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -39,15 +37,14 @@
class PrimaryBouncerToLockscreenTransitionViewModel
@Inject
constructor(
- interactor: KeyguardTransitionInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
- stepFlow =
- interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN),
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
@@ -60,9 +57,10 @@
}
val shortcutsAlpha: Flow<Float> =
- interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map {
- it.value
- }
+ transitionAnimation.sharedFlow(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ onStep = { it }
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 0320dec..092f1ed 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -302,9 +302,6 @@
final NavigationBarView navBarView = getNavigationBarView(displayId);
if (navBarView != null) {
navBarView.showPinningEnterExitToast(entering);
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEnterExitToast(entering);
}
}
@@ -314,9 +311,6 @@
final NavigationBarView navBarView = getNavigationBarView(displayId);
if (navBarView != null) {
navBarView.showPinningEscapeToast();
- } else if (displayId == mDisplayTracker.getDefaultDisplayId()
- && mTaskbarDelegate.isInitialized()) {
- mTaskbarDelegate.showPinningEscapeToast();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 62c7343..0167287 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -504,6 +504,11 @@
}
@Override
+ public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ mOverviewProxyService.onNavigationBarLumaSamplingEnabled(displayId, enable);
+ }
+
+ @Override
public void showPinningEnterExitToast(boolean entering) {
updateSysuiFlags();
if (mScreenPinningNotify == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 45917e8..fd53423 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -1037,6 +1037,19 @@
}
}
+ public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onNavigationBarLumaSamplingEnabled(displayId, enable);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy to enable/disable nav bar luma"
+ + "sampling");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onNavigationBarLumaSamplingEnabled()", e);
+ }
+ }
+
private void updateEnabledState() {
final int currentUser = mUserTracker.getUserId();
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index bd43307..7aa0dad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -143,6 +144,7 @@
channel.enableVibration(true);
mNotificationManager.createNotificationChannel(channel);
+ int currentUid = Process.myUid();
int currentUserId = mUserContextTracker.getUserContext().getUserId();
UserHandle currentUser = new UserHandle(currentUserId);
switch (action) {
@@ -166,7 +168,7 @@
mRecorder = new ScreenMediaRecorder(
mUserContextTracker.getUserContext(),
mMainHandler,
- currentUserId,
+ currentUid,
mAudioSource,
captureTarget,
this
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 3aab3bf..a170d0da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -83,7 +83,7 @@
private Surface mInputSurface;
private VirtualDisplay mVirtualDisplay;
private MediaRecorder mMediaRecorder;
- private int mUser;
+ private int mUid;
private ScreenRecordingMuxer mMuxer;
private ScreenInternalAudioRecorder mAudio;
private ScreenRecordingAudioSource mAudioSource;
@@ -94,12 +94,12 @@
ScreenMediaRecorderListener mListener;
public ScreenMediaRecorder(Context context, Handler handler,
- int user, ScreenRecordingAudioSource audioSource,
+ int uid, ScreenRecordingAudioSource audioSource,
MediaProjectionCaptureTarget captureRegion,
ScreenMediaRecorderListener listener) {
mContext = context;
mHandler = handler;
- mUser = user;
+ mUid = uid;
mCaptureRegion = captureRegion;
mListener = listener;
mAudioSource = audioSource;
@@ -111,7 +111,7 @@
IMediaProjectionManager mediaService =
IMediaProjectionManager.Stub.asInterface(b);
IMediaProjection proj = null;
- proj = mediaService.createProjection(mUser, mContext.getPackageName(),
+ proj = mediaService.createProjection(mUid, mContext.getPackageName(),
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder());
if (mCaptureRegion != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 30a445f..703b3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -118,15 +118,25 @@
private void updateVpn() {
boolean vpnVisible = mSecurityController.isVpnEnabled();
- int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
+ int vpnIconId = currentVpnIconId(
+ mSecurityController.isVpnBranded(),
+ mSecurityController.isVpnValidated());
mIconController.setIcon(mSlotVpn, vpnIconId,
mContext.getResources().getString(R.string.accessibility_vpn_on));
mIconController.setIconVisibility(mSlotVpn, vpnVisible);
}
- private int currentVpnIconId(boolean isBranded) {
- return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
+ private int currentVpnIconId(boolean isBranded, boolean isValidated) {
+ if (isBranded) {
+ return isValidated
+ ? R.drawable.stat_sys_branded_vpn
+ : R.drawable.stat_sys_no_internet_branded_vpn;
+ } else {
+ return isValidated
+ ? R.drawable.stat_sys_vpn_ic
+ : R.drawable.stat_sys_no_internet_vpn_ic;
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 8fc8b2f..de46a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -19,7 +19,7 @@
import android.os.OutcomeReceiver
import android.telephony.satellite.NtnSignalStrengthCallback
import android.telephony.satellite.SatelliteManager
-import android.telephony.satellite.SatelliteStateCallback
+import android.telephony.satellite.SatelliteModemStateCallback
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -180,7 +180,7 @@
// By using the SupportedSatelliteManager here, we expect registration never to fail
private fun connectionStateFlow(sm: SupportedSatelliteManager): Flow<SatelliteConnectionState> =
conflatedCallbackFlow {
- val cb = SatelliteStateCallback { state ->
+ val cb = SatelliteModemStateCallback { state ->
trySend(SatelliteConnectionState.fromModemState(state))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 3be14bc..10bf068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -48,6 +48,8 @@
boolean isNetworkLoggingEnabled();
boolean isVpnEnabled();
boolean isVpnRestricted();
+ /** Whether the VPN network is validated. */
+ boolean isVpnValidated();
/** Whether the VPN app should use branded VPN iconography. */
boolean isVpnBranded();
String getPrimaryVpnName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 5d69f36..9f4a906 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.policy;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+
import android.annotation.Nullable;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
@@ -32,7 +35,9 @@
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.VpnManager;
import android.os.Handler;
@@ -76,7 +81,10 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final NetworkRequest REQUEST =
- new NetworkRequest.Builder().clearCapabilities().build();
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_VPN)
+ .build();
private static final int NO_NETWORK = -1;
private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
@@ -99,6 +107,8 @@
private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
private int mCurrentUserId;
private int mVpnUserId;
+ @GuardedBy("mNetworkProperties")
+ private final SparseArray<NetworkProperties> mNetworkProperties = new SparseArray<>();
// Key: userId, Value: whether the user has CACerts installed
// Needs to be cached here since the query has to be asynchronous
@@ -162,6 +172,21 @@
pw.print(mCurrentVpns.valueAt(i).user);
}
pw.println("}");
+ pw.print(" mNetworkProperties={");
+ synchronized (mNetworkProperties) {
+ for (int i = 0; i < mNetworkProperties.size(); ++i) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ pw.print(mNetworkProperties.keyAt(i));
+ pw.print("={");
+ pw.print(mNetworkProperties.valueAt(i).interfaceName);
+ pw.print(", ");
+ pw.print(mNetworkProperties.valueAt(i).validated);
+ pw.print("}");
+ }
+ }
+ pw.println("}");
}
@Override
@@ -304,6 +329,26 @@
}
@Override
+ public boolean isVpnValidated() {
+ // Prioritize reporting the network status of the parent user.
+ final VpnConfig primaryVpnConfig = mCurrentVpns.get(mVpnUserId);
+ if (primaryVpnConfig != null) {
+ return getVpnValidationStatus(primaryVpnConfig);
+ }
+ // Identify any Unvalidated status in each active VPN network within other profiles.
+ for (int profileId : mUserManager.getEnabledProfileIds(mVpnUserId)) {
+ final VpnConfig vpnConfig = mCurrentVpns.get(profileId);
+ if (vpnConfig == null) {
+ continue;
+ }
+ if (!getVpnValidationStatus(vpnConfig)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
public boolean hasCACertInCurrentUser() {
Boolean hasCACerts = mHasCACerts.get(mCurrentUserId);
return hasCACerts != null && hasCACerts.booleanValue();
@@ -493,11 +538,74 @@
@Override
public void onLost(Network network) {
if (DEBUG) Log.d(TAG, "onLost " + network.getNetId());
+ synchronized (mNetworkProperties) {
+ mNetworkProperties.delete(network.getNetId());
+ }
updateState();
fireCallbacks();
};
+
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ if (DEBUG) Log.d(TAG, "onCapabilitiesChanged " + network.getNetId());
+ final NetworkProperties properties;
+ synchronized (mNetworkProperties) {
+ properties = mNetworkProperties.get(network.getNetId());
+ }
+ // When a new network appears, the system first notifies the application about
+ // its capabilities through onCapabilitiesChanged. This initial notification
+ // will be skipped because the interface information is included in the
+ // subsequent onLinkPropertiesChanged call. After validating the network, the
+ // system might send another onCapabilitiesChanged notification if the network
+ // becomes validated.
+ if (properties == null) {
+ return;
+ }
+ final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ if (properties.validated != validated) {
+ properties.validated = validated;
+ fireCallbacks();
+ }
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
+ if (DEBUG) Log.d(TAG, "onLinkPropertiesChanged " + network.getNetId());
+ final String interfaceName = linkProperties.getInterfaceName();
+ if (interfaceName == null) {
+ Log.w(TAG, "onLinkPropertiesChanged event with null interface");
+ return;
+ }
+ synchronized (mNetworkProperties) {
+ final NetworkProperties properties = mNetworkProperties.get(network.getNetId());
+ if (properties == null) {
+ mNetworkProperties.put(
+ network.getNetId(),
+ new NetworkProperties(interfaceName, false));
+ } else {
+ properties.interfaceName = interfaceName;
+ }
+ }
+ }
};
+ /**
+ * Retrieve the validation status of the VPN network associated with the given VpnConfig.
+ */
+ private boolean getVpnValidationStatus(@NonNull VpnConfig vpnConfig) {
+ synchronized (mNetworkProperties) {
+ // Find the network has the same interface as the VpnConfig
+ for (int i = 0; i < mNetworkProperties.size(); ++i) {
+ if (mNetworkProperties.valueAt(i).interfaceName.equals(vpnConfig.interfaze)) {
+ return mNetworkProperties.valueAt(i).validated;
+ }
+ }
+ }
+ // If no matching network is found, consider it validated.
+ return true;
+ }
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
@@ -508,4 +616,17 @@
}
}
};
+
+ /**
+ * A data class to hold specific Network properties received through the NetworkCallback.
+ */
+ private static class NetworkProperties {
+ public String interfaceName;
+ public boolean validated;
+
+ NetworkProperties(@NonNull String interfaceName, boolean validated) {
+ this.interfaceName = interfaceName;
+ this.validated = validated;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
new file mode 100644
index 0000000..9dd337e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class MenuNotificationFactoryTest extends SysuiTestCase {
+ private MenuNotificationFactory mMenuNotificationFactory;
+
+ @Before
+ public void setUp() {
+ mMenuNotificationFactory = new MenuNotificationFactory(mContext);
+ }
+
+ @Test
+ public void createHiddenNotification_hasUndoAndDeleteAction() {
+ Notification notification = mMenuNotificationFactory.createHiddenNotification();
+
+ assertThat(notification.contentIntent.getIntent().getAction()).isEqualTo(
+ MenuNotificationFactory.ACTION_UNDO);
+ assertThat(notification.deleteIntent.getIntent().getAction()).isEqualTo(
+ MenuNotificationFactory.ACTION_DELETE);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index be6f3ff..68879a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -21,15 +21,30 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_DELETE;
+import static com.android.systemui.accessibility.floatingmenu.MenuNotificationFactory.ACTION_UNDO;
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
+
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -40,6 +55,8 @@
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -53,16 +70,21 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.test.filters.SmallTest;
+import com.android.internal.messages.nano.SystemMessageProto;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -98,6 +120,12 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Spy
+ private SysuiTestableContext mSpyContext = getContext();
@Mock
private IAccessibilityFloatingMenu mFloatingMenu;
@@ -110,8 +138,12 @@
@Mock
private AccessibilityManager mStubAccessibilityManager;
+ private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+
@Before
public void setUp() throws Exception {
+ mSpyContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager);
+
final Rect mDisplayBounds = new Rect();
mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
DISPLAY_WINDOW_HEIGHT);
@@ -119,31 +151,31 @@
new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
- mMenuViewLayer = new MenuViewLayer(mContext, mStubWindowManager, mStubAccessibilityManager,
- mFloatingMenu, mSecureSettings);
+ mMenuViewLayer = new MenuViewLayer(mSpyContext, mStubWindowManager,
+ mStubAccessibilityManager, mFloatingMenu, mSecureSettings);
mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
mMenuAnimationController = mMenuView.getMenuAnimationController();
mLastAccessibilityButtonTargets =
- Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
mLastEnabledAccessibilityServices =
- Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, UserHandle.USER_CURRENT);
mMenuViewLayer.onAttachedToWindow();
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "", UserHandle.USER_CURRENT);
}
@After
public void tearDown() throws Exception {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, mLastAccessibilityButtonTargets,
UserHandle.USER_CURRENT);
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, mLastEnabledAccessibilityServices,
UserHandle.USER_CURRENT);
@@ -188,7 +220,7 @@
setupEnabledAccessibilityServiceList();
mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getString(mContext.getContentResolver(),
+ final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
assertThat(value).isEqualTo("");
@@ -203,7 +235,7 @@
AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY)).thenReturn(stubShortcutTargets);
mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getString(mContext.getContentResolver(),
+ final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
@@ -278,9 +310,60 @@
assertThat(mMenuView.getTranslationX()).isEqualTo(beforePosition.x);
assertThat(mMenuView.getTranslationY()).isEqualTo(beforePosition.y);
}
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+ public void onReleasedInTarget_hideMenuAndShowNotificationWithExpectedActions() {
+ dragMenuThenReleasedInTarget();
+
+ verify(mMockNotificationManager).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN),
+ any(Notification.class));
+ ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(
+ IntentFilter.class);
+ verify(mSpyContext).registerReceiver(
+ any(BroadcastReceiver.class),
+ intentFilterCaptor.capture(),
+ anyInt());
+ assertThat(intentFilterCaptor.getValue().matchAction(ACTION_UNDO)).isTrue();
+ assertThat(intentFilterCaptor.getValue().matchAction(ACTION_DELETE)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+ public void receiveActionUndo_dismissNotificationAndMenuVisible() {
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ dragMenuThenReleasedInTarget();
+
+ verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
+ any(IntentFilter.class), anyInt());
+ broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_UNDO));
+
+ verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
+ verify(mMockNotificationManager).cancel(
+ SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+ assertThat(mMenuView.getVisibility()).isEqualTo(VISIBLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_HIDE)
+ public void receiveActionDelete_dismissNotificationAndHideMenu() {
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ dragMenuThenReleasedInTarget();
+
+ verify(mSpyContext).registerReceiver(broadcastReceiverCaptor.capture(),
+ any(IntentFilter.class), anyInt());
+ broadcastReceiverCaptor.getValue().onReceive(mSpyContext, new Intent(ACTION_DELETE));
+
+ verify(mSpyContext).unregisterReceiver(broadcastReceiverCaptor.getValue());
+ verify(mMockNotificationManager).cancel(
+ SystemMessageProto.SystemMessage.NOTE_A11Y_FLOATING_MENU_HIDDEN);
+ verify(mFloatingMenu).hide();
+ }
private void setupEnabledAccessibilityServiceList() {
- Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.putString(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
@@ -344,6 +427,12 @@
springAnimation.skipToEnd();
springAnimation.doAnimationFrame(500);
});
+ }
+ private void dragMenuThenReleasedInTarget() {
+ MagnetizedObject.MagnetListener magnetListener =
+ mMenuViewLayer.getDragToInteractAnimationController().getMagnetListener();
+ magnetListener.onReleasedInTarget(
+ new MagnetizedObject.MagneticTarget(mock(View.class), 200));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index edd781d..2d9d5ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -20,14 +20,16 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
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.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -37,23 +39,21 @@
@SmallTest
@RunWith(JUnit4::class)
class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
- private lateinit var underTest: KeyguardTransitionAnimationFlow.SharedFlowBuilder
- private lateinit var repository: FakeKeyguardTransitionRepository
- private lateinit var testScope: TestScope
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+ val animationFlow = kosmos.keyguardTransitionAnimationFlow
+ val repository = kosmos.fakeKeyguardTransitionRepository
+
+ private lateinit var underTest: KeyguardTransitionAnimationFlow.FlowBuilder
@Before
fun setUp() {
- testScope = TestScope()
- repository = FakeKeyguardTransitionRepository()
underTest =
- KeyguardTransitionAnimationFlow(
- testScope.backgroundScope,
- mock(),
- )
- .setup(
- duration = 1000.milliseconds,
- stepFlow = repository.transitions,
- )
+ animationFlow.setup(
+ duration = 1000.milliseconds,
+ from = KeyguardState.GONE,
+ to = KeyguardState.DREAMING,
+ )
}
@Test(expected = IllegalArgumentException::class)
@@ -83,6 +83,8 @@
onFinish = { 10f },
)
var animationValues = collectLastValue(flow)
+ runCurrent()
+
repository.sendTransitionStep(step(1f, TransitionState.FINISHED), validateStep = false)
assertThat(animationValues()).isEqualTo(10f)
}
@@ -97,6 +99,8 @@
onCancel = { 100f },
)
var animationValues = collectLastValue(flow)
+ runCurrent()
+
repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
assertThat(animationValues()).isEqualTo(100f)
}
@@ -111,6 +115,8 @@
onStep = { it },
)
var animationValues = collectLastValue(flow)
+ runCurrent()
+
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(animationValues()).isEqualTo(0f)
@@ -137,6 +143,8 @@
onStep = { it },
)
var animationValues = collectLastValue(flow)
+ runCurrent()
+
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertFloat(animationValues(), EMPHASIZED_ACCELERATE.getInterpolation(0f))
repository.sendTransitionStep(step(0.5f, TransitionState.RUNNING))
@@ -157,17 +165,56 @@
duration = 1000.milliseconds,
onStep = { it * 2 },
)
- var animationValues = collectLastValue(flow)
+ val animationValues by collectLastValue(flow)
+ runCurrent()
+
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- assertFloat(animationValues(), 0f)
+ assertFloat(animationValues, 0f)
repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
- assertFloat(animationValues(), 0.6f)
+ assertFloat(animationValues, 0.6f)
repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
- assertFloat(animationValues(), 1.2f)
+ assertFloat(animationValues, 1.2f)
repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
- assertFloat(animationValues(), 1.6f)
+ assertFloat(animationValues, 1.6f)
repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
- assertFloat(animationValues(), 2f)
+ assertFloat(animationValues, 2f)
+ }
+
+ @Test
+ fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() =
+ testScope.runTest {
+ val flow =
+ underTest.sharedFlow(
+ duration = 1000.milliseconds,
+ onStep = { it },
+ )
+ val values by collectValues(flow)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+ repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+
+ assertThat(values.size).isEqualTo(1)
+ assertThat(values[0]).isEqualTo(0.3f)
+ }
+
+ @Test
+ fun sameFloatValueWithADifferentTransitionStateDoesEmitTwice() =
+ testScope.runTest {
+ val flow =
+ underTest.sharedFlow(
+ duration = 1000.milliseconds,
+ onStep = { it },
+ )
+ val values by collectValues(flow)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0.3f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+
+ assertThat(values.size).isEqualTo(2)
+ assertThat(values[0]).isEqualTo(0.3f)
+ assertThat(values[0]).isEqualTo(0.3f)
}
private fun assertFloat(actual: Float?, expected: Float) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index d959872..87391cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -31,6 +31,7 @@
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -50,6 +51,7 @@
fun transitionToAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
+ runCurrent()
transitionRepository.sendTransitionSteps(
listOf(
@@ -69,17 +71,17 @@
fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
+ runCurrent()
transitionRepository.sendTransitionSteps(
listOf(
- stepToAlternateBouncer(0f, TransitionState.STARTED),
- stepToAlternateBouncer(.4f),
- stepToAlternateBouncer(.6f),
- stepToAlternateBouncer(1f),
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
),
testScope,
)
-
assertThat(scrimAlphas.size).isEqualTo(4)
scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
}
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
index af8d8a8..795e68d 100644
--- 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
@@ -30,6 +30,7 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,6 +64,7 @@
fingerprintPropertyRepository.supportsUdfps()
val deviceEntryBackgroundViewAlpha by
collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ runCurrent()
// fade in
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index daafe12..75994da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -39,6 +39,7 @@
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -78,6 +79,8 @@
fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ runCurrent()
+
shadeRepository.setLockscreenShadeExpansion(1f)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
@@ -101,6 +104,8 @@
fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ runCurrent()
+
shadeRepository.setLockscreenShadeExpansion(0f)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
@@ -123,6 +128,7 @@
fun scrimBehindAlpha_leaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ runCurrent()
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
@@ -146,6 +152,8 @@
fun scrimBehindAlpha_doNotLeaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
+ runCurrent()
+
keyguardTransitionRepository.sendTransitionSteps(
listOf(
step(0f, TransitionState.STARTED),
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
index dd542d4..471029b 100644
--- 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
@@ -20,18 +20,15 @@
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.data.repository.fakeKeyguardTransitionRepository
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.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
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
@@ -39,29 +36,10 @@
@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() {
- testScope = TestScope()
- repository = FakeKeyguardTransitionRepository()
- underTest =
- DozingToLockscreenTransitionViewModel(
- interactor =
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- repository = repository,
- )
- .keyguardTransitionInteractor,
- animationFlow =
- KeyguardTransitionAnimationFlow(
- scope = testScope.backgroundScope,
- logger = mock()
- ),
- )
- }
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+ val repository = kosmos.fakeKeyguardTransitionRepository
+ val underTest = kosmos.dozingToLockscreenTransitionViewModel
@Test
fun deviceEntryParentViewShows() =
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 a105008..1c9c942 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
@@ -31,6 +31,7 @@
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,6 +53,7 @@
val pixels = -100f
val enterFromTopTranslationY by
collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+ runCurrent()
// The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -72,6 +74,7 @@
fun enterFromTopAnimationAlpha() =
testScope.runTest {
val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
+ runCurrent()
// The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -92,6 +95,7 @@
testScope.runTest {
val deviceEntryBackgroundViewAlpha by
collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ runCurrent()
// immediately 0f
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -113,6 +117,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// animation doesn't start until the end
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -137,6 +142,7 @@
fingerprintPropertyRepository.supportsRearFps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// animation doesn't start until the end
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -161,6 +167,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// animation doesn't start until the end
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
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
index 5e62317..1912987 100644
--- 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
@@ -30,6 +30,7 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,6 +52,7 @@
testScope.runTest {
val deviceEntryBackgroundViewAlpha by
collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ runCurrent()
// immediately 0f
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -72,6 +74,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// immediately 1f
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -96,6 +99,7 @@
fingerprintPropertyRepository.supportsRearFps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// no updates
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -120,6 +124,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// no updates
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
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
index 9729022..c55c27c 100644
--- 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
@@ -30,6 +30,7 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,6 +55,7 @@
fingerprintPropertyRepository.supportsUdfps()
val deviceEntryBackgroundViewAlpha by
collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ runCurrent()
// immediately 0f
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -75,6 +77,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -92,6 +95,7 @@
fingerprintPropertyRepository.supportsRearFps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
// animation doesn't start until the end
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
@@ -116,6 +120,7 @@
fingerprintPropertyRepository.supportsUdfps()
biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+ runCurrent()
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(deviceEntryParentViewAlpha).isNull()
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
index 2c6436e..0796af0 100644
--- 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
@@ -30,6 +30,7 @@
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,6 +72,7 @@
testScope.runTest {
fingerprintPropertyRepository.supportsUdfps()
val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+ runCurrent()
// immediately 1f
keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ee27c5c..64fd80d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
+import java.util.function.BiConsumer
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import kotlinx.coroutines.CoroutineDispatcher
@@ -100,10 +101,7 @@
override fun toString() = "Manager[$tag]"
override fun getPackage(): String = mComponentName.getPackageName()
override fun getComponentName(): ComponentName = mComponentName
-
- private var isDebug: Boolean = false
- override fun getIsDebug(): Boolean = isDebug
- override fun setIsDebug(value: Boolean) { isDebug = value }
+ override fun setLogFunc(func: BiConsumer<String, String>) { }
override fun loadPlugin() {
if (!mIsLoaded) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index bc50c25..3defee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -112,7 +112,7 @@
mPluginInstance = mPluginInstanceFactory.create(
mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME,
TestPlugin.class, mPluginListener);
- mPluginInstance.setIsDebug(true);
+ mPluginInstance.setLogFunc((tag, msg) -> Log.d((String) tag, (String) msg));
mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index a906a89..02e6fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -31,7 +31,7 @@
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
import android.telephony.satellite.SatelliteManager.SatelliteException
-import android.telephony.satellite.SatelliteStateCallback
+import android.telephony.satellite.SatelliteModemStateCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -106,7 +106,7 @@
val latest by collectLastValue(underTest.connectionState)
runCurrent()
val callback =
- withArgCaptor<SatelliteStateCallback> {
+ withArgCaptor<SatelliteModemStateCallback> {
verify(satelliteManager).registerForSatelliteModemStateChanged(any(), capture())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 1dab84e..cb6ce68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -214,7 +215,8 @@
public void testNetworkRequest() {
verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
(NetworkRequest request) ->
- request.equals(new NetworkRequest.Builder().clearCapabilities().build())
+ request.equals(new NetworkRequest.Builder()
+ .clearCapabilities().addTransportType(TRANSPORT_VPN).build())
), any(NetworkCallback.class));
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index eb287ee..95ff889 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -35,7 +35,8 @@
@JvmStatic
fun create(
testScope: TestScope = TestScope(),
- communalRepository: FakeCommunalRepository = FakeCommunalRepository(),
+ communalRepository: FakeCommunalRepository =
+ FakeCommunalRepository(testScope.backgroundScope),
widgetRepository: FakeCommunalWidgetRepository =
FakeCommunalWidgetRepository(testScope.backgroundScope),
mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(),
@@ -51,6 +52,7 @@
communalRepository = communalRepository,
)
return WithDependencies(
+ testScope,
communalRepository,
widgetRepository,
mediaRepository,
@@ -74,6 +76,7 @@
}
data class WithDependencies(
+ val testScope: TestScope,
val communalRepository: FakeCommunalRepository,
val widgetRepository: FakeCommunalWidgetRepository,
val mediaRepository: FakeCommunalMediaRepository,
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 a94ca29..0c1dbfe 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
@@ -147,7 +147,6 @@
)
}
}
-
_transitions.emit(step)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
index 8d6529a..dad1887 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.ui
import com.android.keyguard.logging.keyguardTransitionAnimationLogger
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -27,6 +28,7 @@
val Kosmos.keyguardTransitionAnimationFlow by Fixture {
KeyguardTransitionAnimationFlow(
scope = applicationCoroutineScope,
+ transitionInteractor = keyguardTransitionInteractor,
logger = keyguardTransitionAnimationLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
index d9c6e4f..3ed9392 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.alternateBouncerToAodTransitionViewModel by Fixture {
AlternateBouncerToAodTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
index e4821b0..c909dd6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.alternateBouncerToGoneTransitionViewModel by Fixture {
AlternateBouncerToGoneTransitionViewModel(
- interactor = keyguardTransitionInteractor,
bouncerToGoneFlows = bouncerToGoneFlows,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index 9f0466d..b4f1218 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,7 +27,6 @@
val Kosmos.alternateBouncerViewModel by Fixture {
AlternateBouncerViewModel(
statusBarKeyguardViewManager = statusBarKeyguardViewManager,
- transitionInteractor = keyguardTransitionInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
index 44e5426..b6f278c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.aodToGoneTransitionViewModel by Fixture {
AodToGoneTransitionViewModel(
- interactor = keyguardTransitionInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index b5a5f03..733340c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
index 27ad0f0..8d066fc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.aodToOccludedTransitionViewModel by Fixture {
AodToOccludedTransitionViewModel(
- interactor = keyguardTransitionInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
index 6ffcc9a..c71c1c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt
@@ -20,7 +20,6 @@
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -31,7 +30,6 @@
val Kosmos.bouncerToGoneFlows by Fixture {
BouncerToGoneFlows(
- interactor = keyguardTransitionInteractor,
statusBarStateController = sysuiStatusBarStateController,
primaryBouncerInteractor = primaryBouncerInteractor,
keyguardDismissActionInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..400a0d8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.dozingToLockscreenTransitionViewModel by Fixture {
+ DozingToLockscreenTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
index 00ece14..19e4241 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
var Kosmos.goneToAodTransitionViewModel by Fixture {
GoneToAodTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
index 073b34b..b267a96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.goneToDreamingTransitionViewModel by Fixture {
GoneToDreamingTransitionViewModel(
- interactor = keyguardTransitionInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
index 7865f71..07b4cd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.lockscreenToAodTransitionViewModel by Fixture {
LockscreenToAodTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
shadeDependentFlows = shadeDependentFlows,
animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
index b9f4b71..56d5ff6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.lockscreenToDreamingTransitionViewModel by Fixture {
LockscreenToDreamingTransitionViewModel(
- interactor = keyguardTransitionInteractor,
shadeDependentFlows = shadeDependentFlows,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
index 475aa2d..1b2337f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.lockscreenToGoneTransitionViewModel by Fixture {
LockscreenToGoneTransitionViewModel(
- interactor = keyguardTransitionInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
index 8541a4f..9953d39 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.lockscreenToOccludedTransitionViewModel by Fixture {
LockscreenToOccludedTransitionViewModel(
- interactor = keyguardTransitionInteractor,
shadeDependentFlows = shadeDependentFlows,
configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
index 65c47fc..f094f22 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.lockscreenToPrimaryBouncerTransitionViewModel by Fixture {
LockscreenToPrimaryBouncerTransitionViewModel(
- interactor = keyguardTransitionInteractor,
shadeDependentFlows = shadeDependentFlows,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
index ddde549..b7867b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.occludedToAodTransitionViewModel by Fixture {
OccludedToAodTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
index 93ecb79..e6651a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelKosmos.kt
@@ -20,7 +20,6 @@
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,7 +27,6 @@
var Kosmos.occludedToLockscreenTransitionViewModel by Fixture {
OccludedToLockscreenTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
index a7f29d6..8d88730 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.primaryBouncerToAodTransitionViewModel by Fixture {
PrimaryBouncerToAodTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
index ace6ae3..ab28d0d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt
@@ -20,7 +20,6 @@
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -30,7 +29,6 @@
val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture {
PrimaryBouncerToGoneTransitionViewModel(
- interactor = keyguardTransitionInteractor,
statusBarStateController = sysuiStatusBarStateController,
primaryBouncerInteractor = primaryBouncerInteractor,
keyguardDismissActionInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
index 3bbabf7..8566251 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelKosmos.kt
@@ -19,7 +19,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.primaryBouncerToLockscreenTransitionViewModel by Fixture {
PrimaryBouncerToLockscreenTransitionViewModel(
- interactor = keyguardTransitionInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
index 021e7df..ac90a45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSecurityController.kt
@@ -77,6 +77,8 @@
override fun isVpnBranded(): Boolean = fakeState.isVpnBranded
+ override fun isVpnValidated(): Boolean = fakeState.isVpnValidated
+
override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName
override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName
@@ -110,6 +112,7 @@
var isVpnEnabled: Boolean = false,
var isVpnRestricted: Boolean = false,
var isVpnBranded: Boolean = false,
+ var isVpnValidated: Boolean = false,
var primaryVpnName: String? = null,
var workProfileVpnName: String? = null,
var hasCACertInCurrentUser: Boolean = false,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 76199e3..791165d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -109,6 +109,11 @@
}
@Override
+ public boolean isVpnValidated() {
+ return false;
+ }
+
+ @Override
public String getPrimaryVpnName() {
return null;
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index b403a7f..7f542d1 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -408,5 +408,9 @@
// Notify the user about external display events related to screenshot.
// Package: com.android.systemui
NOTE_GLOBAL_SCREENSHOT_EXTERNAL_DISPLAY = 1008;
+
+ // Notify the user that accessibility floating menu is hidden.
+ // Package: com.android.systemui
+ NOTE_A11Y_FLOATING_MENU_HIDDEN = 1009;
}
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index d695d36..549fa36 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -7,4 +7,12 @@
"restore for apps that have been launched."
bug: "308401499"
is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_max_size_writes_to_pipes"
+ namespace: "onboarding"
+ description: "Enables the write buffer to pipes to be of maximum size."
+ bug: "265976737"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 6aed9aa..cca166b 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -40,8 +40,8 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.Flags;
import com.android.server.backup.FullBackupJob;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.OperationStorage.OpState;
@@ -390,8 +390,11 @@
// Set up to send data to the transport
final int N = mPackages.size();
- final int chunkSizeInBytes =
- BackupAndRestoreFeatureFlags.getFullBackupWriteToTransportBufferSizeBytes();
+ int chunkSizeInBytes = 8 * 1024; // 8KB
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ chunkSizeInBytes = 64 * 1024; // 64KB
+ }
final byte[] buffer = new byte[chunkSizeInBytes];
for (int i = 0; i < N; i++) {
mBackupRunner = null;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index ff72476..2c9eb51 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -29,7 +29,6 @@
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations;
-import android.app.backup.BackupManager;
import android.app.backup.FullBackup;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IFullBackupRestoreObserver;
@@ -51,6 +50,7 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
import com.android.server.backup.KeyValueAdbRestoreEngine;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.OperationStorage.OpType;
@@ -157,13 +157,19 @@
mMonitor = monitor;
mOnlyPackage = onlyPackage;
mAllowApks = allowApks;
- mBuffer = new byte[32 * 1024];
mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mIsAdbRestore = isAdbRestore;
mUserId = backupManagerService.getUserId();
mBackupEligibilityRules = backupEligibilityRules;
+
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ mBuffer = new byte[64 * 1024]; // 64KB
+ } else {
+ mBuffer = new byte[32 * 1024];
+ }
}
@VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 316a16d..2fbc3cd 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -968,7 +968,12 @@
throws Exception {
Set<String> excludedKeysForPackage = getExcludedKeysForPackage(packageName);
- byte[] buffer = new byte[8192]; // will grow when needed
+ int bufferSize = 8192; // 8KB
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ bufferSize = 64 * 1024; // 64KB
+ }
+ byte[] buffer = new byte[bufferSize]; // will grow when needed
while (in.readNextHeader()) {
final String key = in.getKey();
final int size = in.getDataSize();
@@ -1116,7 +1121,11 @@
ParcelFileDescriptor tReadEnd = mTransportPipes[0];
ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
- int bufferSize = 32 * 1024;
+ int bufferSize = 32 * 1024; // 32KB
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ bufferSize = 64 * 1024; // 64KB
+ }
byte[] buffer = new byte[bufferSize];
FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index 1c0cd87..843354e 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -21,7 +21,7 @@
import android.os.ParcelFileDescriptor;
import android.util.Slog;
-import com.android.server.backup.BackupAndRestoreFeatureFlags;
+import com.android.server.backup.Flags;
import java.io.DataInputStream;
import java.io.EOFException;
@@ -46,8 +46,11 @@
// We do not take close() responsibility for the pipe FD
FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
DataInputStream in = new DataInputStream(raw);
- final int chunkSizeInBytes =
- BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes();
+ int chunkSizeInBytes = 32 * 1024; // 32KB
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ chunkSizeInBytes = 64 * 1024; // 64KB
+ }
byte[] buffer = new byte[chunkSizeInBytes];
int chunkTotal;
while ((chunkTotal = in.readInt()) > 0) {
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index 0accb9f..5a8533a 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -40,6 +40,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
import com.android.server.backup.restore.RestoreDeleteObserver;
import com.android.server.backup.restore.RestorePolicy;
@@ -93,7 +94,12 @@
try (Session session = installer.openSession(sessionId)) {
try (OutputStream apkStream = session.openWrite(info.packageName, 0,
info.size)) {
- byte[] buffer = new byte[32 * 1024];
+ int bufferSize = 32 * 1024; // 32KB
+ if (Flags.enableMaxSizeWritesToPipes()) {
+ // Linux pipe capacity (buffer size) is 16 pages where each page is 4KB
+ bufferSize = 64 * 1024; // 64KB
+ }
+ byte[] buffer = new byte[bufferSize];
long size = info.size;
while (size > 0) {
long toRead = (buffer.length < size) ? buffer.length : size;
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 720687e..0e66fbc 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -23,12 +23,12 @@
import android.os.Build;
import android.util.Slog;
-import com.google.security.cryptauth.lib.securegcm.BadHandleException;
-import com.google.security.cryptauth.lib.securegcm.CryptoException;
-import com.google.security.cryptauth.lib.securegcm.D2DConnectionContextV1;
-import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext;
-import com.google.security.cryptauth.lib.securegcm.D2DHandshakeContext.Role;
-import com.google.security.cryptauth.lib.securegcm.HandshakeException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext;
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role;
+import com.google.security.cryptauth.lib.securegcm.ukey2.HandshakeException;
import libcore.io.IoUtils;
import libcore.io.Streams;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9eb35fd..eb6fdd7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -101,6 +101,7 @@
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -2679,6 +2680,14 @@
if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
return;
}
+ if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ // TelephonyManager.getEmergencyNumberList() throws an exception if
+ // FEATURE_TELEPHONY_CALLING is not defined.
+ return;
+ }
+ }
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index e0a2246..9fc0bf9 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -63,6 +63,11 @@
private static final long CONSECUTIVE_ANR_TIME_MS = TimeUnit.MINUTES.toMillis(2);
/**
+ * Time to wait before taking dumps for other processes to reduce load at boot time.
+ */
+ private static final long SELF_ONLY_AFTER_BOOT_MS = TimeUnit.MINUTES.toMillis(10);
+
+ /**
* The keep alive time for the threads in the helper threadpool executor
*/
private static final int DEFAULT_THREAD_KEEP_ALIVE_SECOND = 10;
@@ -231,7 +236,8 @@
// If there are many ANR at the same time, the latency may be larger.
// If the latency is too large, the stack trace might not be meaningful.
final long reportLatency = startTime - r.mTimestamp;
- final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;
+ final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS
+ || startTime < SELF_ONLY_AFTER_BOOT_MS;
r.appNotResponding(onlyDumpSelf);
final long endTime = SystemClock.uptimeMillis();
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 9f31f37..5f12ce1 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -73,7 +73,8 @@
private static final Set<Integer> DEFAULT_EVENT_SET = Sets.newHashSet(
AmbientContextEvent.EVENT_COUGH,
AmbientContextEvent.EVENT_SNORE,
- AmbientContextEvent.EVENT_BACK_DOUBLE_TAP);
+ AmbientContextEvent.EVENT_BACK_DOUBLE_TAP,
+ AmbientContextEvent.EVENT_HEART_RATE);
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 91d533c..4cbee2b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8835,6 +8835,8 @@
synchronized (VolumeStreamState.class) {
oldIndex = getIndex(device);
index = getValidIndex(index, hasModifyAudioSettings);
+ // for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index
+ int aliasIndex = index;
if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
index = mIndexMax;
}
@@ -8853,7 +8855,8 @@
if (streamType != mStreamType &&
mStreamVolumeAlias[streamType] == mStreamType &&
(changed || !aliasStreamState.hasIndexForDevice(device))) {
- final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
+ final int scaledIndex =
+ rescaleIndex(aliasIndex, mStreamType, streamType);
aliasStreamState.setIndex(scaledIndex, device, caller,
hasModifyAudioSettings);
if (isCurrentDevice) {
@@ -9375,6 +9378,14 @@
if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {
return;
}
+
+ // Persisting STREAM_SYSTEM_ENFORCED index is not needed as its alias (STREAM_RING)
+ // is persisted. This can also be problematic when the enforcement is active as it will
+ // override current SYSTEM_RING persisted value given they share the same settings name
+ // (due to aliasing).
+ if (streamState.mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) {
+ return;
+ }
if (streamState.hasValidSettingsName()) {
mSettings.putSystemIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index aa638aa..e507c6b 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -6,5 +6,8 @@
fstern@google.com
cosminbaies@google.com
+# Automotive
+kanant@google.com
+
ogunwale@google.com #{LAST_RESORT_SUGGESTION}
jjaggi@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index c772e08..5df0de8 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -22,12 +22,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.Flags;
import com.android.server.FgThread;
import java.util.Objects;
@@ -104,10 +106,26 @@
boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
&& (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs;
- return mIsInEmergencyCall
- || isInExtensionTime
- || mTelephonyManager.getEmergencyCallbackMode()
- || mTelephonyManager.isInEmergencySmsMode();
+ if (!Flags.enforceTelephonyFeatureMapping()) {
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || mTelephonyManager.getEmergencyCallbackMode()
+ || mTelephonyManager.isInEmergencySmsMode();
+ } else {
+ boolean emergencyCallbackMode = false;
+ boolean emergencySmsMode = false;
+ PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ }
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || emergencyCallbackMode
+ || emergencySmsMode;
+ }
}
private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 6deda46..f6571d9 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -653,14 +653,14 @@
}
@Override // Binder call
- public boolean hasProjectionPermission(int uid, String packageName) {
+ public boolean hasProjectionPermission(int processUid, String packageName) {
final long token = Binder.clearCallingIdentity();
boolean hasPermission = false;
try {
hasPermission |= checkPermission(packageName,
android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
|| mAppOps.noteOpNoThrow(
- AppOpsManager.OP_PROJECT_MEDIA, uid, packageName)
+ AppOpsManager.OP_PROJECT_MEDIA, processUid, packageName)
== AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(token);
@@ -669,7 +669,7 @@
}
@Override // Binder call
- public IMediaProjection createProjection(int uid, String packageName, int type,
+ public IMediaProjection createProjection(int processUid, String packageName, int type,
boolean isPermanentGrant) {
if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)
!= PackageManager.PERMISSION_GRANTED) {
@@ -680,13 +680,13 @@
throw new IllegalArgumentException("package name must not be empty");
}
final UserHandle callingUser = Binder.getCallingUserHandle();
- return createProjectionInternal(uid, packageName, type, isPermanentGrant,
+ return createProjectionInternal(processUid, packageName, type, isPermanentGrant,
callingUser);
}
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public IMediaProjection getProjection(int uid, String packageName) {
+ public IMediaProjection getProjection(int processUid, String packageName) {
getProjection_enforcePermission();
if (packageName == null || packageName.isEmpty()) {
throw new IllegalArgumentException("package name must not be empty");
@@ -695,7 +695,7 @@
MediaProjection projection;
final long callingToken = Binder.clearCallingIdentity();
try {
- projection = getProjectionInternal(uid, packageName);
+ projection = getProjectionInternal(processUid, packageName);
} finally {
Binder.restoreCallingIdentity(callingToken);
}
@@ -869,12 +869,13 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
+ public void notifyPermissionRequestInitiated(
+ int hostProcessUid, int sessionCreationSource) {
notifyPermissionRequestInitiated_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
MediaProjectionManagerService.this.notifyPermissionRequestInitiated(
- hostUid, sessionCreationSource);
+ hostProcessUid, sessionCreationSource);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -882,11 +883,11 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyPermissionRequestDisplayed(int hostUid) {
+ public void notifyPermissionRequestDisplayed(int hostProcessUid) {
notifyPermissionRequestDisplayed_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
- MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostUid);
+ MediaProjectionManagerService.this.notifyPermissionRequestDisplayed(hostProcessUid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -894,11 +895,11 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyPermissionRequestCancelled(int hostUid) {
+ public void notifyPermissionRequestCancelled(int hostProcessUid) {
notifyPermissionRequestCancelled_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
- MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid);
+ MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostProcessUid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -906,11 +907,11 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyAppSelectorDisplayed(int hostUid) {
+ public void notifyAppSelectorDisplayed(int hostProcessUid) {
notifyAppSelectorDisplayed_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
- MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostUid);
+ MediaProjectionManagerService.this.notifyAppSelectorDisplayed(hostProcessUid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -919,12 +920,12 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void notifyWindowingModeChanged(
- int contentToRecord, int targetUid, int windowingMode) {
+ int contentToRecord, int targetProcessUid, int windowingMode) {
notifyWindowingModeChanged_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
MediaProjectionManagerService.this.notifyWindowingModeChanged(
- contentToRecord, targetUid, windowingMode);
+ contentToRecord, targetProcessUid, windowingMode);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index e3880c3..deb95d8 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -108,7 +108,7 @@
for (int i = 0; i < pendingPackageRemovals.size(); i++) {
userHistory.onPackageRemoved(pendingPackageRemovals.get(i));
}
- mUserPendingPackageRemovals.put(userId, null);
+ mUserPendingPackageRemovals.remove(userId);
}
// delete history if it was disabled when the user was locked
@@ -133,7 +133,7 @@
synchronized (mLock) {
// Actual data deletion is handled by other parts of the system (the entire directory is
// removed) - we just need clean up our internal state for GC
- mUserPendingPackageRemovals.put(userId, null);
+ mUserPendingPackageRemovals.remove(userId);
mHistoryEnabled.put(userId, false);
mUserPendingHistoryDisables.put(userId, false);
onUserStopped(userId);
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
new file mode 100644
index 0000000..4454601
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.pm;
+
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.NonNull;
+import android.app.BackgroundInstallControlManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+public class BackgroundInstallControlCallbackHelper {
+
+ @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+ @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId";
+ private static final String TAG = "BackgroundInstallControlCallbackHelper";
+
+ private final Handler mHandler;
+
+ BackgroundInstallControlCallbackHelper() {
+ HandlerThread backgroundThread =
+ new ServiceThread(
+ "BackgroundInstallControlCallbackHelperBg",
+ THREAD_PRIORITY_BACKGROUND,
+ true);
+ backgroundThread.start();
+ mHandler = new Handler(backgroundThread.getLooper());
+ }
+
+ @NonNull @VisibleForTesting
+ final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
+
+ /** Registers callback that gets invoked upon detection of an MBA
+ *
+ * NOTE: The callback is user context agnostic and currently broadcasts to all users of other
+ * users app installs. This is fine because the API is for SystemServer use only.
+ */
+ public void registerBackgroundInstallCallback(IRemoteCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.register(callback, null);
+ }
+ }
+
+ /** Unregisters callback */
+ public void unregisterBackgroundInstallCallback(IRemoteCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.unregister(callback);
+ }
+ }
+
+ /**
+ * Invokes all registered callbacks Callbacks are processed through user provided-threads and
+ * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent
+ */
+ public void notifyAllCallbacks(int userId, String packageName) {
+ Bundle extras = new Bundle();
+ extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName);
+ extras.putInt(FLAGGED_USER_ID_KEY, userId);
+ synchronized (mCallbacks) {
+ mHandler.post(
+ () ->
+ mCallbacks.broadcast(
+ callback -> {
+ try {
+ callback.sendResult(extras);
+ } catch (RemoteException e) {
+ Slog.e(
+ TAG,
+ "error detected: " + e.getLocalizedMessage(),
+ e);
+ }
+ }));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 7f0aadc..3a9dedc 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -16,7 +16,12 @@
package com.android.server.pm;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
@@ -30,6 +35,7 @@
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
@@ -69,8 +75,10 @@
private static final String DISK_FILE_NAME = "states";
private static final String DISK_DIR_NAME = "bic";
- private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10;
+ private static final String ENFORCE_PERMISSION_ERROR_MSG =
+ "User is not permitted to call service: ";
+ private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10;
private static final int MSG_USAGE_EVENT_RECEIVED = 0;
private static final int MSG_PACKAGE_ADDED = 1;
private static final int MSG_PACKAGE_REMOVED = 2;
@@ -78,19 +86,20 @@
private final Context mContext;
private final BinderService mBinderService;
private final PackageManager mPackageManager;
+ // TODO migrate all internal PackageManager calls to PackageManagerInternal where possible.
+ // b/310983905
private final PackageManagerInternal mPackageManagerInternal;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
private final Handler mHandler;
private final File mDiskFile;
-
private SparseSetArray<String> mBackgroundInstalledPackages = null;
+ private final BackgroundInstallControlCallbackHelper mCallbackHelper;
// User ID -> package name -> set of foreground time frame
- private final SparseArrayMap<String,
- TreeSet<ForegroundTimeFrame>> mInstallerForegroundTimeFrames =
- new SparseArrayMap<>();
+ private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
+ mInstallerForegroundTimeFrames = new SparseArrayMap<>();
public BackgroundInstallControlService(@NonNull Context context) {
this(new InjectorImpl(context));
@@ -106,13 +115,11 @@
mHandler = new EventHandler(injector.getLooper(), this);
mDiskFile = injector.getDiskFile();
mUsageStatsManagerInternal = injector.getUsageStatsManagerInternal();
+ mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper();
mUsageStatsManagerInternal.registerListener(
(userId, event) ->
- mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED,
- userId,
- 0,
- event).sendToTarget()
- );
+ mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, userId, 0, event)
+ .sendToTarget());
mBinderService = new BinderService(this);
}
@@ -126,12 +133,15 @@
@Override
public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ mService.enforceCallerQueryPackagesPermissions();
if (!Build.IS_DEBUGGABLE) {
return mService.getBackgroundInstalledPackages(flags, userId);
}
// The debug.transparency.bg-install-apps (only works for debuggable builds)
// is used to set mock list of background installed apps for testing.
// The list of apps' names is delimited by ",".
+ // TODO: Remove after migrating test to new background install method using
+ // {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905
String propertyString = SystemProperties.get("debug.transparency.bg-install-apps");
if (TextUtils.isEmpty(propertyString)) {
return mService.getBackgroundInstalledPackages(flags, userId);
@@ -139,16 +149,41 @@
return mService.getMockBackgroundInstalledPackages(propertyString);
}
}
+
+ @Override
+ public void registerBackgroundInstallCallback(IRemoteCallback callback) {
+ mService.enforceCallerQueryPackagesPermissions();
+ mService.enforceCallerInteractCrossUserPermissions();
+ mService.mCallbackHelper.registerBackgroundInstallCallback(callback);
+ }
+
+ @Override
+ public void unregisterBackgroundInstallCallback(IRemoteCallback callback) {
+ mService.enforceCallerQueryPackagesPermissions();
+ mService.enforceCallerInteractCrossUserPermissions();
+ mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback);
+ }
+ }
+
+ @RequiresPermission(QUERY_ALL_PACKAGES)
+ void enforceCallerQueryPackagesPermissions() throws SecurityException {
+ mContext.enforceCallingPermission(QUERY_ALL_PACKAGES,
+ ENFORCE_PERMISSION_ERROR_MSG + QUERY_ALL_PACKAGES);
+ }
+
+ @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+ void enforceCallerInteractCrossUserPermissions() throws SecurityException {
+ mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL,
+ ENFORCE_PERMISSION_ERROR_MSG + INTERACT_ACROSS_USERS_FULL);
}
@VisibleForTesting
ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.PackageInfoFlags.of(flags), userId);
+ PackageManager.PackageInfoFlags.of(flags), userId);
initBackgroundInstalledPackages();
-
ListIterator<PackageInfo> iter = packages.listIterator();
while (iter.hasNext()) {
String packageName = iter.next().packageName;
@@ -170,8 +205,9 @@
List<PackageInfo> mockPackages = new ArrayList<>();
for (String name : mockPackageNames) {
try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(name,
- PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL));
+ PackageInfo packageInfo =
+ mPackageManager.getPackageInfo(
+ name, PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL));
mockPackages.add(packageInfo);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Package's PackageInfo not found " + name);
@@ -192,18 +228,16 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_USAGE_EVENT_RECEIVED: {
- mService.handleUsageEvent((UsageEvents.Event) msg.obj, msg.arg1 /* userId */);
+ case MSG_USAGE_EVENT_RECEIVED:
+ mService.handleUsageEvent(
+ (UsageEvents.Event) msg.obj, msg.arg1 /* userId */);
break;
- }
- case MSG_PACKAGE_ADDED: {
+ case MSG_PACKAGE_ADDED:
mService.handlePackageAdd((String) msg.obj, msg.arg1 /* userId */);
break;
- }
- case MSG_PACKAGE_REMOVED: {
+ case MSG_PACKAGE_REMOVED:
mService.handlePackageRemove((String) msg.obj, msg.arg1 /* userId */);
break;
- }
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -213,8 +247,9 @@
void handlePackageAdd(String packageName, int userId) {
ApplicationInfo appInfo = null;
try {
- appInfo = mPackageManager.getApplicationInfoAsUser(packageName,
- PackageManager.ApplicationInfoFlags.of(0), userId);
+ appInfo =
+ mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.ApplicationInfoFlags.of(0), userId);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Package's appInfo not found " + packageName);
return;
@@ -233,15 +268,18 @@
// the installers without INSTALL_PACKAGES perm can't perform
// the installation in background. So we can just filter out them.
- if (mPermissionManager.checkPermission(installerPackageName,
- android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT,
- userId) != PackageManager.PERMISSION_GRANTED) {
+ if (mPermissionManager.checkPermission(
+ installerPackageName,
+ android.Manifest.permission.INSTALL_PACKAGES,
+ Context.DEVICE_ID_DEFAULT,
+ userId)
+ != PERMISSION_GRANTED) {
return;
}
// convert up-time to current time.
- final long installTimestamp = System.currentTimeMillis()
- - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
+ final long installTimestamp =
+ System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
if (installedByAdb(initiatingPackageName)
|| wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -250,6 +288,7 @@
initBackgroundInstalledPackages();
mBackgroundInstalledPackages.add(userId, packageName);
+ mCallbackHelper.notifyAllCallbacks(userId, packageName);
writeBackgroundInstalledPackagesToDisk();
}
@@ -259,8 +298,8 @@
return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName);
}
- private boolean wasForegroundInstallation(String installerPackageName,
- int userId, long installTimestamp) {
+ private boolean wasForegroundInstallation(
+ String installerPackageName, int userId, long installTimestamp) {
TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames =
mInstallerForegroundTimeFrames.get(userId, installerPackageName);
@@ -349,12 +388,12 @@
for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) {
int userId = mBackgroundInstalledPackages.keyAt(i);
for (String packageName : mBackgroundInstalledPackages.get(userId)) {
- long token = protoOutputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token =
+ protoOutputStream.start(
+ BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
protoOutputStream.write(
BackgroundInstalledPackageProto.PACKAGE_NAME, packageName);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.USER_ID, userId + 1);
+ protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1);
protoOutputStream.end(token);
}
}
@@ -387,23 +426,28 @@
!= (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) {
continue;
}
- long token = protoInputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token =
+ protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
String packageName = null;
int userId = UserHandle.USER_NULL;
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (protoInputStream.getFieldNumber()) {
case (int) BackgroundInstalledPackageProto.PACKAGE_NAME:
- packageName = protoInputStream.readString(
- BackgroundInstalledPackageProto.PACKAGE_NAME);
+ packageName =
+ protoInputStream.readString(
+ BackgroundInstalledPackageProto.PACKAGE_NAME);
break;
case (int) BackgroundInstalledPackageProto.USER_ID:
- userId = protoInputStream.readInt(
- BackgroundInstalledPackageProto.USER_ID) - 1;
+ userId =
+ protoInputStream.readInt(
+ BackgroundInstalledPackageProto.USER_ID)
+ - 1;
break;
default:
- Slog.w(TAG, "Undefined field in proto: "
- + protoInputStream.getFieldNumber());
+ Slog.w(
+ TAG,
+ "Undefined field in proto: "
+ + protoInputStream.getFieldNumber());
}
}
protoInputStream.end(token);
@@ -432,9 +476,12 @@
if (mInstallerForegroundTimeFrames.contains(userId, pkgName)) {
return true;
}
- return mPermissionManager.checkPermission(pkgName,
- android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT,
- userId) == PackageManager.PERMISSION_GRANTED;
+ return mPermissionManager.checkPermission(
+ pkgName,
+ android.Manifest.permission.INSTALL_PACKAGES,
+ Context.DEVICE_ID_DEFAULT,
+ userId)
+ == PERMISSION_GRANTED;
}
@Override
@@ -448,21 +495,22 @@
publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
}
- mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- mHandler.obtainMessage(MSG_PACKAGE_ADDED,
- userId, 0, packageName).sendToTarget();
- }
+ mPackageManagerInternal.getPackageList(
+ new PackageManagerInternal.PackageListObserver() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
+ .sendToTarget();
+ }
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- mHandler.obtainMessage(MSG_PACKAGE_REMOVED,
- userId, 0, packageName).sendToTarget();
- }
- });
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
+ .sendToTarget();
+ }
+ });
}
// The foreground time frame (ForegroundTimeFrame) represents the period
@@ -518,7 +566,7 @@
}
/**
- * Dependency injector for {@link #BackgroundInstallControlService)}.
+ * Dependency injector for {@link BackgroundInstallControlService}.
*/
interface Injector {
Context getContext();
@@ -534,6 +582,8 @@
Looper getLooper();
File getDiskFile();
+
+ BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper();
}
private static final class InjectorImpl implements Injector {
@@ -570,11 +620,11 @@
@Override
public Looper getLooper() {
- ServiceThread serviceThread = new ServiceThread(TAG,
- android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+ ServiceThread serviceThread =
+ new ServiceThread(
+ TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
serviceThread.start();
return serviceThread.getLooper();
-
}
@Override
@@ -583,5 +633,10 @@
File file = new File(dir, DISK_FILE_NAME);
return file;
}
+
+ @Override
+ public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() {
+ return new BackgroundInstallControlCallbackHelper();
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 49af4fe..c1b7489 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2337,8 +2337,18 @@
final long identity = Binder.clearCallingIdentity();
try {
final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
- if (telecomManager != null && telecomManager.isInCall()) {
- flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+ if (com.android.internal.telephony.flags
+ .Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELECOM)) {
+ if (telecomManager != null && telecomManager.isInCall()) {
+ flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+ }
+ }
+ } else {
+ if (telecomManager != null && telecomManager.isInCall()) {
+ flags |= UserManager.SWITCHABILITY_STATUS_USER_IN_CALL;
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8d93408..13f1141 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1279,8 +1279,8 @@
ipw.println();
}
- PackageWatchdog.getInstance(mContext).dump(ipw);
});
+ PackageWatchdog.getInstance(mContext).dump(ipw);
}
@AnyThread
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 58acbe0..c0e3308 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1339,16 +1339,22 @@
String inputId = mHardwareInputIdMap.get(deviceId);
if (inputId != null) {
- if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
- if (previousCableConnectionStatus != connection.getInputStateLocked()) {
- mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- connection.getInputStateLocked(), 0, inputId).sendToTarget();
- }
- } else {
- if ((previousConfigsLength == 0)
- != (connection.getConfigsLengthLocked() == 0)) {
- mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- connection.getInputStateLocked(), 0, inputId).sendToTarget();
+ synchronized (mLock) {
+ if (connection.updateCableConnectionStatusLocked(
+ cableConnectionStatus)) {
+ if (previousCableConnectionStatus
+ != connection.getInputStateLocked()) {
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId)
+ .sendToTarget();
+ }
+ } else {
+ if ((previousConfigsLength == 0)
+ != (connection.getConfigsLengthLocked() == 0)) {
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId)
+ .sendToTarget();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 9213d96..6ce8685 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -74,6 +74,10 @@
return mFeatureFlags;
}
+ public boolean isFlagSafeModeTimeoutConfigEnabled() {
+ return mFeatureFlags.safeModeTimeoutConfig();
+ }
+
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 54c97dd..fcc0de1 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -915,9 +915,11 @@
// TODO(b/180132994): explore safely removing this Thread check
mVcnContext.ensureRunningOnLooperThread();
- logInfo(
- "Selected underlying network changed: "
- + (underlying == null ? null : underlying.network));
+ if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) {
+ logInfo(
+ "Selected underlying network changed: "
+ + (underlying == null ? null : underlying.network));
+ }
// TODO(b/179091925): Move the delayed-message handling to BaseState
@@ -1242,9 +1244,28 @@
createScheduledAlarm(
SAFEMODE_TIMEOUT_ALARM,
delayedMessage,
- mVcnContext.isInTestMode()
- ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE)
- : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+ getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup));
+ }
+
+ /** Gets the safe mode timeout */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static long getSafeModeTimeoutMs(
+ VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) {
+ final int defaultSeconds =
+ vcnContext.isInTestMode()
+ ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE
+ : SAFEMODE_TIMEOUT_SECONDS;
+
+ final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp);
+ int resultSeconds = defaultSeconds;
+
+ if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) {
+ resultSeconds =
+ carrierConfig.getInt(
+ VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds);
+ }
+
+ return TimeUnit.SECONDS.toMillis(resultSeconds);
}
private void cancelSafeModeAlarm() {
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 7f129ea..d32e5cc 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -47,7 +47,6 @@
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
/** @hide */
@@ -86,7 +85,6 @@
* <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
* template as the underlying network.
*/
- @VisibleForTesting(visibility = Visibility.PRIVATE)
static final int PRIORITY_INVALID = -1;
/** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
@@ -96,7 +94,7 @@
List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
PersistableBundleWrapper carrierConfig) {
// mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
@@ -118,7 +116,7 @@
networkRecord,
subscriptionGroup,
snapshot,
- currentlySelected,
+ isSelected,
carrierConfig)) {
return priorityIndex;
}
@@ -140,12 +138,9 @@
UnderlyingNetworkRecord networkRecord,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
- final boolean isSelectedUnderlyingNetwork =
- currentlySelected != null
- && Objects.equals(currentlySelected.network, networkRecord.network);
final int meteredMatch = networkPriority.getMetered();
final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -159,7 +154,7 @@
if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
|| (caps.getLinkUpstreamBandwidthKbps()
< networkPriority.getMinEntryUpstreamBandwidthKbps()
- && !isSelectedUnderlyingNetwork)) {
+ && !isSelected)) {
return false;
}
@@ -167,7 +162,7 @@
< networkPriority.getMinExitDownstreamBandwidthKbps()
|| (caps.getLinkDownstreamBandwidthKbps()
< networkPriority.getMinEntryDownstreamBandwidthKbps()
- && !isSelectedUnderlyingNetwork)) {
+ && !isSelected)) {
return false;
}
@@ -191,7 +186,7 @@
return checkMatchesWifiPriorityRule(
(VcnWifiUnderlyingNetworkTemplate) networkPriority,
networkRecord,
- currentlySelected,
+ isSelected,
carrierConfig);
}
@@ -214,7 +209,7 @@
public static boolean checkMatchesWifiPriorityRule(
VcnWifiUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
@@ -223,7 +218,7 @@
}
// TODO: Move the Network Quality check to the network metric monitor framework.
- if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
+ if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) {
return false;
}
@@ -237,15 +232,11 @@
private static boolean isWifiRssiAcceptable(
UnderlyingNetworkRecord networkRecord,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
- final boolean isSelectedNetwork =
- currentlySelected != null
- && networkRecord.network.equals(currentlySelected.network);
- if (isSelectedNetwork
- && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
+ if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
return true;
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 6afa795..48df44b 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -48,6 +48,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
@@ -83,6 +84,9 @@
@NonNull private final TelephonyCallback mActiveDataSubIdListener =
new VcnActiveDataSubscriptionIdListener();
+ private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords =
+ new ArrayMap<>();
+
@NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
@Nullable private NetworkCallback mWifiBringupCallback;
@Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
@@ -105,7 +109,8 @@
this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
}
- private UnderlyingNetworkController(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkController(
@NonNull VcnContext vcnContext,
@NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull ParcelUuid subscriptionGroup,
@@ -196,6 +201,7 @@
NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
mCellBringupCallbacks.clear();
+ mUnderlyingNetworkRecords.clear();
// Register new callbacks. Make-before-break; always register new callbacks before removal
// of old callbacks
@@ -395,6 +401,18 @@
// Update carrier config
mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
+ // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier
+ // config to calculate their cached priority classes. For simplicity, the
+ // UnderlyingNetworkController does not listen for changes in VCN-related carrier config
+ // keys, and changes are applied at restart of the VcnGatewayConnection
+ for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+ evaluator.reevaluate(
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig);
+ }
+
// Only trigger re-registration if subIds in this group have changed
if (oldSnapshot
.getAllSubIdsInGroup(mSubscriptionGroup)
@@ -418,32 +436,62 @@
.unregisterTelephonyCallback(mActiveDataSubIdListener);
}
+ private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
+ TreeSet<UnderlyingNetworkEvaluator> sorted =
+ new TreeSet<>(UnderlyingNetworkEvaluator.getComparator());
+
+ for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+ if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
+ sorted.add(evaluator);
+ }
+ }
+
+ return sorted;
+ }
+
private void reevaluateNetworks() {
if (mIsQuitting || mRouteSelectionCallback == null) {
return; // UnderlyingNetworkController has quit.
}
- TreeSet<UnderlyingNetworkRecord> sorted =
- mRouteSelectionCallback.getSortedUnderlyingNetworks();
- UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
+ TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
+
+ UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
+ UnderlyingNetworkRecord candidate =
+ candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord();
if (Objects.equals(mCurrentRecord, candidate)) {
return;
}
String allNetworkPriorities = "";
- for (UnderlyingNetworkRecord record : sorted) {
+ for (UnderlyingNetworkEvaluator recordEvaluator : sorted) {
if (!allNetworkPriorities.isEmpty()) {
allNetworkPriorities += ", ";
}
- allNetworkPriorities += record.network + ": " + record.priorityClass;
+ allNetworkPriorities +=
+ recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass();
}
- logInfo(
- "Selected network changed to "
- + (candidate == null ? null : candidate.network)
- + ", selected from list: "
- + allNetworkPriorities);
+
+ if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) {
+ logInfo(
+ "Selected network changed to "
+ + (candidate == null ? null : candidate.network)
+ + ", selected from list: "
+ + allNetworkPriorities);
+ }
+
mCurrentRecord = candidate;
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
+
+ // Need to update all evaluators to ensure the previously selected one is unselected
+ for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+ evaluator.setIsSelected(
+ candidateEvaluator == evaluator,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig);
+ }
}
/**
@@ -463,46 +511,26 @@
*/
@VisibleForTesting
class UnderlyingNetworkListener extends NetworkCallback {
- private final Map<Network, UnderlyingNetworkRecord.Builder>
- mUnderlyingNetworkRecordBuilders = new ArrayMap<>();
-
UnderlyingNetworkListener() {
super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
}
- private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() {
- TreeSet<UnderlyingNetworkRecord> sorted =
- new TreeSet<>(UnderlyingNetworkRecord.getComparator());
-
- for (UnderlyingNetworkRecord.Builder builder :
- mUnderlyingNetworkRecordBuilders.values()) {
- if (builder.isValid()) {
- final UnderlyingNetworkRecord record =
- builder.build(
- mVcnContext,
- mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
- mSubscriptionGroup,
- mLastSnapshot,
- mCurrentRecord,
- mCarrierConfig);
- if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
- sorted.add(record);
- }
- }
- }
-
- return sorted;
- }
-
@Override
public void onAvailable(@NonNull Network network) {
- mUnderlyingNetworkRecordBuilders.put(
- network, new UnderlyingNetworkRecord.Builder(network));
+ mUnderlyingNetworkRecords.put(
+ network,
+ mDeps.newUnderlyingNetworkEvaluator(
+ mVcnContext,
+ network,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig));
}
@Override
public void onLost(@NonNull Network network) {
- mUnderlyingNetworkRecordBuilders.remove(network);
+ mUnderlyingNetworkRecords.remove(network);
reevaluateNetworks();
}
@@ -510,15 +538,20 @@
@Override
public void onCapabilitiesChanged(
@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
- final UnderlyingNetworkRecord.Builder builder =
- mUnderlyingNetworkRecordBuilders.get(network);
- if (builder == null) {
+ final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+ if (evaluator == null) {
logWtf("Got capabilities change for unknown key: " + network);
return;
}
- builder.setNetworkCapabilities(networkCapabilities);
- if (builder.isValid()) {
+ evaluator.setNetworkCapabilities(
+ networkCapabilities,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig);
+
+ if (evaluator.isValid()) {
reevaluateNetworks();
}
}
@@ -526,30 +559,40 @@
@Override
public void onLinkPropertiesChanged(
@NonNull Network network, @NonNull LinkProperties linkProperties) {
- final UnderlyingNetworkRecord.Builder builder =
- mUnderlyingNetworkRecordBuilders.get(network);
- if (builder == null) {
+ final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+ if (evaluator == null) {
logWtf("Got link properties change for unknown key: " + network);
return;
}
- builder.setLinkProperties(linkProperties);
- if (builder.isValid()) {
+ evaluator.setLinkProperties(
+ linkProperties,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig);
+
+ if (evaluator.isValid()) {
reevaluateNetworks();
}
}
@Override
public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
- final UnderlyingNetworkRecord.Builder builder =
- mUnderlyingNetworkRecordBuilders.get(network);
- if (builder == null) {
+ final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
+ if (evaluator == null) {
logWtf("Got blocked status change for unknown key: " + network);
return;
}
- builder.setIsBlocked(isBlocked);
- if (builder.isValid()) {
+ evaluator.setIsBlocked(
+ isBlocked,
+ mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
+ mSubscriptionGroup,
+ mLastSnapshot,
+ mCarrierConfig);
+
+ if (evaluator.isValid()) {
reevaluateNetworks();
}
}
@@ -614,16 +657,8 @@
pw.println("Underlying networks:");
pw.increaseIndent();
if (mRouteSelectionCallback != null) {
- for (UnderlyingNetworkRecord record :
- mRouteSelectionCallback.getSortedUnderlyingNetworks()) {
- record.dump(
- mVcnContext,
- pw,
- mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
- mSubscriptionGroup,
- mLastSnapshot,
- mCurrentRecord,
- mCarrierConfig);
+ for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) {
+ recordEvaluator.dump(pw);
}
}
pw.decreaseIndent();
@@ -653,5 +688,23 @@
@Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
}
- private static class Dependencies {}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class Dependencies {
+ /** Construct a new UnderlyingNetworkEvaluator */
+ public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
+ @NonNull VcnContext vcnContext,
+ @NonNull Network network,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ return new UnderlyingNetworkEvaluator(
+ vcnContext,
+ network,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ lastSnapshot,
+ carrierConfig);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
new file mode 100644
index 0000000..c124a19
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -0,0 +1,217 @@
+/*
+ * 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.vcn.routeselection;
+
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.os.ParcelUuid;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for
+ * route selection.
+ *
+ * @hide
+ */
+public class UnderlyingNetworkEvaluator {
+ private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
+
+ @NonNull private final VcnContext mVcnContext;
+ @NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder;
+
+ private boolean mIsSelected;
+ private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID;
+
+ public UnderlyingNetworkEvaluator(
+ @NonNull VcnContext vcnContext,
+ @NonNull Network network,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
+
+ Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates");
+ Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ Objects.requireNonNull(lastSnapshot, "Missing lastSnapshot");
+
+ mNetworkRecordBuilder =
+ new UnderlyingNetworkRecord.Builder(
+ Objects.requireNonNull(network, "Missing network"));
+ mIsSelected = false;
+
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ private void updatePriorityClass(
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ if (mNetworkRecordBuilder.isValid()) {
+ mPriorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ mVcnContext,
+ mNetworkRecordBuilder.build(),
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ lastSnapshot,
+ mIsSelected,
+ carrierConfig);
+ } else {
+ mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID;
+ }
+ }
+
+ public static Comparator<UnderlyingNetworkEvaluator> getComparator() {
+ return (left, right) -> {
+ final int leftIndex = left.mPriorityClass;
+ final int rightIndex = right.mPriorityClass;
+
+ // In the case of networks in the same priority class, prioritize based on other
+ // criteria (eg. actively selected network, link metrics, etc)
+ if (leftIndex == rightIndex) {
+ // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
+ // fall into the same priority class.
+ if (left.mIsSelected) {
+ return -1;
+ }
+ if (right.mIsSelected) {
+ return 1;
+ }
+ }
+ return Integer.compare(leftIndex, rightIndex);
+ };
+ }
+
+ /** Set the NetworkCapabilities */
+ public void setNetworkCapabilities(
+ @NonNull NetworkCapabilities nc,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ mNetworkRecordBuilder.setNetworkCapabilities(nc);
+
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ /** Set the LinkProperties */
+ public void setLinkProperties(
+ @NonNull LinkProperties lp,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ mNetworkRecordBuilder.setLinkProperties(lp);
+
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ /** Set whether the network is blocked */
+ public void setIsBlocked(
+ boolean isBlocked,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ mNetworkRecordBuilder.setIsBlocked(isBlocked);
+
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ /** Set whether the network is selected as VCN's underlying network */
+ public void setIsSelected(
+ boolean isSelected,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ mIsSelected = isSelected;
+
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ /**
+ * Update the last TelephonySubscriptionSnapshot and carrier config to reevaluate the network
+ */
+ public void reevaluate(
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot lastSnapshot,
+ @Nullable PersistableBundleWrapper carrierConfig) {
+ updatePriorityClass(
+ underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
+ }
+
+ /** Return whether this network evaluator is valid */
+ public boolean isValid() {
+ return mNetworkRecordBuilder.isValid();
+ }
+
+ /** Return the network */
+ public Network getNetwork() {
+ return mNetworkRecordBuilder.getNetwork();
+ }
+
+ /** Return the network record */
+ public UnderlyingNetworkRecord getNetworkRecord() {
+ return mNetworkRecordBuilder.build();
+ }
+
+ /** Return the priority class for network selection */
+ public int getPriorityClass() {
+ return mPriorityClass;
+ }
+
+ /** Dump the information of this instance */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("UnderlyingNetworkEvaluator:");
+ pw.increaseIndent();
+
+ if (mNetworkRecordBuilder.isValid()) {
+ getNetworkRecord().dump(pw);
+ } else {
+ pw.println(
+ "UnderlyingNetworkRecord incomplete: mNetwork: "
+ + mNetworkRecordBuilder.getNetwork());
+ }
+
+ pw.println("mIsSelected: " + mIsSelected);
+ pw.println("mPriorityClass: " + mPriorityClass);
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index aea9f4d..7ab8e55 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -16,24 +16,17 @@
package com.android.server.vcn.routeselection;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.vcn.VcnUnderlyingNetworkTemplate;
-import android.os.ParcelUuid;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.VcnContext;
-import java.util.Comparator;
-import java.util.List;
import java.util.Objects;
/**
@@ -46,54 +39,17 @@
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
public final boolean isBlocked;
- public final boolean isSelected;
- public final int priorityClass;
@VisibleForTesting(visibility = Visibility.PRIVATE)
public UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean isBlocked,
- VcnContext vcnContext,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundleWrapper carrierConfig) {
+ boolean isBlocked) {
this.network = network;
this.networkCapabilities = networkCapabilities;
this.linkProperties = linkProperties;
this.isBlocked = isBlocked;
-
- this.isSelected = isSelected(this.network, currentlySelected);
-
- priorityClass =
- NetworkPriorityClassifier.calculatePriorityClass(
- vcnContext,
- this,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
- }
-
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public UnderlyingNetworkRecord(
- @NonNull Network network,
- @NonNull NetworkCapabilities networkCapabilities,
- @NonNull LinkProperties linkProperties,
- boolean isBlocked,
- boolean isSelected,
- int priorityClass) {
- this.network = network;
- this.networkCapabilities = networkCapabilities;
- this.linkProperties = linkProperties;
- this.isBlocked = isBlocked;
- this.isSelected = isSelected;
-
- this.priorityClass = priorityClass;
}
@Override
@@ -113,64 +69,20 @@
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
}
- /** Returns if two records are equal including their priority classes. */
- public static boolean isEqualIncludingPriorities(
- UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) {
- if (left != null && right != null) {
- return left.equals(right)
- && left.isSelected == right.isSelected
- && left.priorityClass == right.priorityClass;
- }
-
- return left == right;
- }
-
- static Comparator<UnderlyingNetworkRecord> getComparator() {
- return (left, right) -> {
- final int leftIndex = left.priorityClass;
- final int rightIndex = right.priorityClass;
-
- // In the case of networks in the same priority class, prioritize based on other
- // criteria (eg. actively selected network, link metrics, etc)
- if (leftIndex == rightIndex) {
- // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
- // fall into the same priority class.
- if (left.isSelected) {
- return -1;
- }
- if (right.isSelected) {
- return 1;
- }
- }
- return Integer.compare(leftIndex, rightIndex);
- };
- }
-
- private static boolean isSelected(
- Network networkToCheck, UnderlyingNetworkRecord currentlySelected) {
- if (currentlySelected == null) {
- return false;
- }
- if (currentlySelected.network.equals(networkToCheck)) {
- return true;
- }
- return false;
+ /** Return whether two records represent the same network */
+ public static boolean isSameNetwork(
+ @Nullable UnderlyingNetworkRecord leftRecord,
+ @Nullable UnderlyingNetworkRecord rightRecord) {
+ final Network left = leftRecord == null ? null : leftRecord.network;
+ final Network right = rightRecord == null ? null : rightRecord.network;
+ return Objects.equals(left, right);
}
/** Dumps the state of this record for logging and debugging purposes. */
- void dump(
- VcnContext vcnContext,
- IndentingPrintWriter pw,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundleWrapper carrierConfig) {
+ void dump(IndentingPrintWriter pw) {
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
- pw.println("priorityClass: " + priorityClass);
- pw.println("isSelected: " + isSelected);
pw.println("mNetwork: " + network);
pw.println("mNetworkCapabilities: " + networkCapabilities);
pw.println("mLinkProperties: " + linkProperties);
@@ -218,29 +130,14 @@
return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
}
- UnderlyingNetworkRecord build(
- VcnContext vcnContext,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- ParcelUuid subscriptionGroup,
- TelephonySubscriptionSnapshot snapshot,
- UnderlyingNetworkRecord currentlySelected,
- PersistableBundleWrapper carrierConfig) {
+ UnderlyingNetworkRecord build() {
if (!isValid()) {
throw new IllegalArgumentException(
"Called build before UnderlyingNetworkRecord was valid");
}
return new UnderlyingNetworkRecord(
- mNetwork,
- mNetworkCapabilities,
- mLinkProperties,
- mIsBlocked,
- vcnContext,
- underlyingNetworkTemplates,
- subscriptionGroup,
- snapshot,
- currentlySelected,
- carrierConfig);
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
}
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b94206d..9e56d7a 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1311,7 +1311,7 @@
Rect insets;
if (mainWindow != null) {
insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
- mBounds, WindowInsets.Type.systemBars(),
+ mBounds, WindowInsets.Type.tappableElement(),
false /* ignoreVisibility */).toRect();
InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
} else {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index fc3a338..9c9cf04 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -120,38 +120,56 @@
static final int BAL_BLOCK = 0;
- static final int BAL_ALLOW_DEFAULT = 1;
+ static final int BAL_ALLOW_DEFAULT =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_DEFAULT;
// Following codes are in order of precedence
/** Important UIDs which should be always allowed to launch activities */
- static final int BAL_ALLOW_ALLOWLISTED_UID = 2;
+ static final int BAL_ALLOW_ALLOWLISTED_UID =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_UID;
/** Apps that fulfill a certain role that can can always launch new tasks */
- static final int BAL_ALLOW_ALLOWLISTED_COMPONENT = 3;
+ static final int BAL_ALLOW_ALLOWLISTED_COMPONENT =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_ALLOWLISTED_COMPONENT;
- /** Apps which currently have a visible window or are bound by a service with a visible
- * window */
- static final int BAL_ALLOW_VISIBLE_WINDOW = 4;
+ /**
+ * Apps which currently have a visible window or are bound by a service with a visible
+ * window
+ */
+ static final int BAL_ALLOW_VISIBLE_WINDOW =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_VISIBLE_WINDOW;
/** Allowed due to the PendingIntent sender */
- static final int BAL_ALLOW_PENDING_INTENT = 5;
+ static final int BAL_ALLOW_PENDING_INTENT =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_PENDING_INTENT;
- /** App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges
- * granted to it */
- static final int BAL_ALLOW_PERMISSION = 6;
+ /**
+ * App has START_ACTIVITIES_FROM_BACKGROUND permission or BAL instrumentation privileges
+ * granted to it
+ */
+ static final int BAL_ALLOW_PERMISSION =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_BAL_PERMISSION;
/** Process has SYSTEM_ALERT_WINDOW permission granted to it */
- static final int BAL_ALLOW_SAW_PERMISSION = 7;
+ static final int BAL_ALLOW_SAW_PERMISSION =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SAW_PERMISSION;
/** App is in grace period after an activity was started or finished */
- static final int BAL_ALLOW_GRACE_PERIOD = 8;
+ static final int BAL_ALLOW_GRACE_PERIOD =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_GRACE_PERIOD;
/** App is in a foreground task or bound to a foreground service (but not itself visible) */
- static final int BAL_ALLOW_FOREGROUND = 9;
+ static final int BAL_ALLOW_FOREGROUND =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_FOREGROUND;
/** Process belongs to a SDK sandbox */
- static final int BAL_ALLOW_SDK_SANDBOX = 10;
+ static final int BAL_ALLOW_SDK_SANDBOX =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_SDK_SANDBOX;
+
+ /** Process belongs to a SDK sandbox */
+ static final int BAL_ALLOW_NON_APP_VISIBLE_WINDOW =
+ FrameworkStatsLog.BAL_ALLOWED__ALLOWED_REASON__BAL_ALLOW_NON_APP_VISIBLE_WINDOW;
static String balCodeToString(@BalCode int balCode) {
return switch (balCode) {
@@ -160,6 +178,7 @@
case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT";
case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND";
case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD";
+ case BAL_ALLOW_NON_APP_VISIBLE_WINDOW -> "BAL_ALLOW_NON_APP_VISIBLE_WINDOW";
case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT";
case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION";
case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION";
@@ -788,7 +807,7 @@
/*background*/ false, "callingUid has visible window");
}
if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
/*background*/ false, "callingUid has non-app visible window");
}
@@ -884,7 +903,7 @@
/*background*/ false, "realCallingUid has visible window");
}
if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
- return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
/*background*/ false, "realCallingUid has non-app visible window");
}
} else {
@@ -989,7 +1008,8 @@
|| balCode == BAL_ALLOW_PERMISSION
|| balCode == BAL_ALLOW_PENDING_INTENT
|| balCode == BAL_ALLOW_SAW_PERMISSION
- || balCode == BAL_ALLOW_VISIBLE_WINDOW) {
+ || balCode == BAL_ALLOW_VISIBLE_WINDOW
+ || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) {
return true;
}
}
@@ -1501,7 +1521,8 @@
Intent intent = state.mIntent;
if (code == BAL_ALLOW_PENDING_INTENT
- && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
+ && (callingUid < Process.FIRST_APPLICATION_UID
+ || realCallingUid < Process.FIRST_APPLICATION_UID)) {
String activityName = intent != null
? requireNonNull(intent.getComponent()).flattenToShortString() : "";
writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT,
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
index 4fcdbfc..e3954355 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -33,6 +33,7 @@
":BackgroundInstallControlServiceTestApp",
":BackgroundInstallControlMockApp1",
":BackgroundInstallControlMockApp2",
+ ":BackgroundInstallControlMockApp3",
],
test_suites: [
"general-tests",
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
index 1e7a78a..031d57f 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
+++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
@@ -29,11 +29,14 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push-file"
- key="BackgroundInstallControlMockApp1.apk"
- value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" />
+ key="BackgroundInstallControlMockApp1.apk"
+ value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" />
<option name="push-file"
- key="BackgroundInstallControlMockApp2.apk"
- value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" />
+ key="BackgroundInstallControlMockApp2.apk"
+ value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" />
+ <option name="push-file"
+ key="BackgroundInstallControlMockApp3.apk"
+ value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
index 7450607..5092a46 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
+++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
@@ -41,17 +41,26 @@
private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk";
private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk";
+ // TODO: Move the silent installs to test-app using {@link
+ // BackgroundInstallControlServiceTest#installPackage(String, String)} and remove deviceConfig
+ // branch in BICS.
+ // b/310983905
@Test
public void testGetMockBackgroundInstalledPackages() throws Exception {
- installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1);
+ installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1);
installPackage(TEST_DATA_DIR + MOCK_APK_FILE_2);
assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull();
assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull();
- assertThat(getDevice().setProperty("debug.transparency.bg-install-apps",
- MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue();
- runDeviceTest("testGetMockBackgroundInstalledPackages");
+ assertThat(
+ getDevice()
+ .setProperty(
+ "debug.transparency.bg-install-apps",
+ MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2))
+ .isTrue();
+ runDeviceTest(
+ "BackgroundInstallControlServiceTest", "testGetMockBackgroundInstalledPackages");
assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull();
assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull();
@@ -59,16 +68,30 @@
assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull();
}
+ @Test
+ public void testRegisterCallback() throws Exception {
+ runDeviceTest(
+ "BackgroundInstallControlServiceTest",
+ "testRegisterBackgroundInstallControlCallback");
+ }
+
+ @Test
+ public void testUnregisterCallback() throws Exception {
+ runDeviceTest(
+ "BackgroundInstallControlServiceTest",
+ "testUnregisterBackgroundInstallControlCallback");
+ }
+
private void installPackage(String path) throws DeviceNotAvailableException {
String cmd = "pm install -t --force-queryable " + path;
CommandResult result = getDevice().executeShellV2Command(cmd);
assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue();
}
- private void runDeviceTest(String method) throws DeviceNotAvailableException {
+ private void runDeviceTest(String testName, String method) throws DeviceNotAvailableException {
var options = new DeviceTestRunOptions(PACKAGE_NAME);
- options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest");
+ options.setTestClassName(PACKAGE_NAME + "." + testName);
options.setTestMethodName(method);
runDeviceTests(options);
}
-}
+}
\ No newline at end of file
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml
index 1fa1f84..b5b8ea0 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml
@@ -21,6 +21,9 @@
<uses-library android:name="android.test.runner" />
</application>
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="APCT tests for background install control service"
android:targetPackage="com.android.server.pm.test.app" />
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
index b74e561..f033fed 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
@@ -16,54 +16,256 @@
package com.android.server.pm.test.app;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
import static com.google.common.truth.Truth.assertThat;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
+import android.util.Pair;
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class BackgroundInstallControlServiceTest {
private static final String TAG = "BackgroundInstallControlServiceTest";
+ private static final String ACTION_INSTALL_COMMIT =
+ "com.android.server.pm.test.app.BackgroundInstallControlServiceTest"
+ + ".ACTION_INSTALL_COMMIT";
+ private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3";
+ private static final String TEST_DATA_DIR = "/data/local/tmp/";
+
+ private static final String MOCK_APK_FILE = "BackgroundInstallControlMockApp3.apk";
private IBackgroundInstallControlService mIBics;
@Before
public void setUp() {
- mIBics = IBackgroundInstallControlService.Stub.asInterface(
- ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
+ mIBics =
+ IBackgroundInstallControlService.Stub.asInterface(
+ ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
assertThat(mIBics).isNotNull();
}
+ @After
+ public void tearDown() {
+ runShellCommand("pm uninstall " + MOCK_PACKAGE_NAME);
+ }
+
@Test
public void testGetMockBackgroundInstalledPackages() throws RemoteException {
- ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages(
- PackageManager.MATCH_ALL,
- UserHandle.USER_ALL);
+ ParceledListSlice<PackageInfo> slice =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mIBics,
+ (bics) -> {
+ try {
+ return bics.getBackgroundInstalledPackages(
+ PackageManager.MATCH_ALL, Process.myUserHandle()
+ .getIdentifier());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ QUERY_ALL_PACKAGES);
assertThat(slice).isNotNull();
var packageList = slice.getList();
assertThat(packageList).isNotNull();
assertThat(packageList).hasSize(2);
- var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1",
- "com.android.servicestests.apps.bicmockapp2");
- var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName)
- .collect(Collectors.toSet());
+ var expectedPackageNames =
+ Set.of(
+ "com.android.servicestests.apps.bicmockapp1",
+ "com.android.servicestests.apps.bicmockapp2");
+ var actualPackageNames =
+ packageList.stream()
+ .map((packageInfo) -> packageInfo.packageName)
+ .collect(Collectors.toSet());
assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames);
}
+
+ @Test
+ public void testRegisterBackgroundInstallControlCallback()
+ throws Exception {
+ String testPackageName = "test";
+ int testUserId = 1;
+ ArrayList<Pair<String, Integer>> sharedResource = new ArrayList<>();
+ IRemoteCallback testCallback =
+ new IRemoteCallback.Stub() {
+ private final ArrayList<Pair<String, Integer>> mArray = sharedResource;
+
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mArray.add(new Pair(testPackageName, testUserId));
+ }
+ };
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mIBics,
+ (bics) -> {
+ try {
+ bics.registerBackgroundInstallCallback(testCallback);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ QUERY_ALL_PACKAGES,
+ INTERACT_ACROSS_USERS_FULL);
+ installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME);
+
+ assertUntil(() -> sharedResource.size() == 1, 2000);
+ assertThat(sharedResource.get(0).first).isEqualTo(testPackageName);
+ assertThat(sharedResource.get(0).second).isEqualTo(testUserId);
+ }
+
+ @Test
+ public void testUnregisterBackgroundInstallControlCallback() {
+ String testValue = "test";
+ ArrayList<String> sharedResource = new ArrayList<>();
+ IRemoteCallback testCallback =
+ new IRemoteCallback.Stub() {
+ private final ArrayList<String> mArray = sharedResource;
+
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mArray.add(testValue);
+ }
+ };
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mIBics,
+ (bics) -> {
+ try {
+ bics.registerBackgroundInstallCallback(testCallback);
+ bics.unregisterBackgroundInstallCallback(testCallback);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ QUERY_ALL_PACKAGES,
+ INTERACT_ACROSS_USERS_FULL);
+ installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME);
+
+ assertUntil(() -> sharedResource.isEmpty(), 2000);
+ }
+
+ private static boolean installPackage(String apkPath, String packageName) {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final CountDownLatch installLatch = new CountDownLatch(1);
+ final BroadcastReceiver installReceiver =
+ new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ int packageInstallStatus =
+ intent.getIntExtra(
+ PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE_INVALID);
+ if (packageInstallStatus == PackageInstaller.STATUS_SUCCESS) {
+ installLatch.countDown();
+ }
+ }
+ };
+ final IntentFilter intentFilter = new IntentFilter(ACTION_INSTALL_COMMIT);
+ context.registerReceiver(installReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionParams params =
+ new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED);
+ try {
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+ OutputStream out = session.openWrite(packageName, 0, -1);
+ FileInputStream fis = new FileInputStream(apkPath);
+ byte[] buffer = new byte[65536];
+ int size;
+ while ((size = fis.read(buffer)) != -1) {
+ out.write(buffer, 0, size);
+ }
+ session.fsync(out);
+ fis.close();
+ out.close();
+
+ runWithShellPermissionIdentity(
+ () -> {
+ session.commit(createPendingIntent(context).getIntentSender());
+ installLatch.await(5, TimeUnit.SECONDS);
+ });
+ return true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static PendingIntent createPendingIntent(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(
+ context,
+ 1,
+ new Intent(ACTION_INSTALL_COMMIT)
+ .setPackage(
+ BackgroundInstallControlServiceTest.class.getPackageName()),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
+ return pendingIntent;
+ }
+
+ private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ try {
+ command.run();
+ } finally {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ }
+
+ private static void assertUntil(Supplier<Boolean> condition, int timeoutMs) {
+ long endTime = System.currentTimeMillis() + timeoutMs;
+ while (System.currentTimeMillis() <= endTime) {
+ if (condition.get()) return;
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ assertThat(condition.get()).isTrue();
+ }
}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
index 7804f4c..39b0ff7 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
@@ -50,3 +50,11 @@
"--rename-manifest-package com.android.servicestests.apps.bicmockapp2",
],
}
+
+android_test_helper_app {
+ name: "BackgroundInstallControlMockApp3",
+ defaults: ["bic-mock-app-defaults"],
+ aaptflags: [
+ "--rename-manifest-package com.android.servicestests.apps.bicmockapp3",
+ ],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
new file mode 100644
index 0000000..e1fce9b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.pm;
+
+import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY;
+import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+/** Unit tests for {@link BackgroundInstallControlCallbackHelperTest} */
+@Presubmit
+@RunWith(JUnit4.class)
+public class BackgroundInstallControlCallbackHelperTest {
+
+ private final IRemoteCallback mCallback =
+ spy(
+ new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle extras) {}
+ });
+
+ private BackgroundInstallControlCallbackHelper mCallbackHelper;
+
+ @Before
+ public void setup() {
+ mCallbackHelper = new BackgroundInstallControlCallbackHelper();
+ }
+
+ @Test
+ public void registerBackgroundInstallControlCallback_registers_successfully() {
+ mCallbackHelper.registerBackgroundInstallCallback(mCallback);
+
+ synchronized (mCallbackHelper.mCallbacks) {
+ assertEquals(1, mCallbackHelper.mCallbacks.getRegisteredCallbackCount());
+ assertEquals(mCallback, mCallbackHelper.mCallbacks.getRegisteredCallbackItem(0));
+ }
+ }
+
+ @Test
+ public void unregisterBackgroundInstallControlCallback_unregisters_successfully() {
+ synchronized (mCallbackHelper.mCallbacks) {
+ mCallbackHelper.mCallbacks.register(mCallback);
+ }
+
+ mCallbackHelper.unregisterBackgroundInstallCallback(mCallback);
+
+ synchronized (mCallbackHelper.mCallbacks) {
+ assertEquals(0, mCallbackHelper.mCallbacks.getRegisteredCallbackCount());
+ }
+ }
+
+ @Test
+ public void notifyAllCallbacks_broadcastsToCallbacks()
+ throws RemoteException {
+ String testPackageName = "testname";
+ int testUserId = 1;
+ mCallbackHelper.registerBackgroundInstallCallback(mCallback);
+
+ mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture());
+ Bundle receivedBundle = bundleCaptor.getValue();
+ assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY));
+ assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index c0051c6..eee3752 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -133,7 +133,8 @@
verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
eq(parentProcess), eq(aboveSystem), eq(timeoutRecord), eq(mAuxExecutorService),
- eq(false) /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/, eq(mEarlyDumpFuture));
+ anyBoolean() /* onlyDumpSelf */, eq(false) /*isContinuousAnr*/,
+ eq(mEarlyDumpFuture));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index daf18ed..3069d25 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -16,6 +16,10 @@
package com.android.server.pm;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -27,6 +31,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -97,7 +102,6 @@
private Looper mLooper;
private File mFile;
-
@Mock
private Context mContext;
@Mock
@@ -108,8 +112,12 @@
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private PermissionManagerServiceInternal mPermissionManager;
+ @Mock
+ private BackgroundInstallControlCallbackHelper mCallbackHelper;
+
@Captor
private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;
+
@Captor
private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
@@ -119,11 +127,12 @@
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
- mFile = new File(
- InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(),
- "test");
- mBackgroundInstallControlService = new BackgroundInstallControlService(
- new MockInjector(mContext));
+ mFile =
+ new File(
+ InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(),
+ "test");
+ mBackgroundInstallControlService =
+ new BackgroundInstallControlService(new MockInjector(mContext));
verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture());
mUsageEventListener = mUsageEventListenerCaptor.getValue();
@@ -143,8 +152,7 @@
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
mBackgroundInstallControlService.initBackgroundInstalledPackages();
assertNotNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- assertEquals(0,
- mBackgroundInstallControlService.getBackgroundInstalledPackages().size());
+ assertEquals(0, mBackgroundInstallControlService.getBackgroundInstalledPackages().size());
}
@Test
@@ -161,12 +169,9 @@
// Write test data to the file on the disk.
try {
ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
- long token = protoOutputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1);
+ long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1);
+ protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1);
protoOutputStream.end(token);
protoOutputStream.flush();
atomicFile.finishWrite(fileOutputStream);
@@ -198,20 +203,14 @@
try {
ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
- long token = protoOutputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1);
+ long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1);
+ protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1);
protoOutputStream.end(token);
- token = protoOutputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2);
- protoOutputStream.write(
- BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1);
+ token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2);
+ protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1);
protoOutputStream.end(token);
protoOutputStream.flush();
@@ -241,7 +240,7 @@
// Read the file on the disk to verify
var packagesInDisk = new SparseSetArray<>();
AtomicFile atomicFile = new AtomicFile(mFile);
- try (FileInputStream fileInputStream = atomicFile.openRead()) {
+ try (FileInputStream fileInputStream = atomicFile.openRead()) {
ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream);
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
@@ -249,23 +248,25 @@
!= (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) {
continue;
}
- long token = protoInputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token =
+ protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
String packageName = null;
int userId = UserHandle.USER_NULL;
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (protoInputStream.getFieldNumber()) {
case (int) BackgroundInstalledPackageProto.PACKAGE_NAME:
- packageName = protoInputStream.readString(
- BackgroundInstalledPackageProto.PACKAGE_NAME);
+ packageName =
+ protoInputStream.readString(
+ BackgroundInstalledPackageProto.PACKAGE_NAME);
break;
case (int) BackgroundInstalledPackageProto.USER_ID:
- userId = protoInputStream.readInt(
- BackgroundInstalledPackageProto.USER_ID) - 1;
+ userId =
+ protoInputStream.readInt(
+ BackgroundInstalledPackageProto.USER_ID)
+ - 1;
break;
default:
- fail("Undefined field in proto: "
- + protoInputStream.getFieldNumber());
+ fail("Undefined field in proto: " + protoInputStream.getFieldNumber());
}
}
protoInputStream.end(token);
@@ -296,7 +297,7 @@
// Read the file on the disk to verify
var packagesInDisk = new SparseSetArray<>();
AtomicFile atomicFile = new AtomicFile(mFile);
- try (FileInputStream fileInputStream = atomicFile.openRead()) {
+ try (FileInputStream fileInputStream = atomicFile.openRead()) {
ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream);
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
@@ -304,23 +305,25 @@
!= (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) {
continue;
}
- long token = protoInputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token =
+ protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
String packageName = null;
int userId = UserHandle.USER_NULL;
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (protoInputStream.getFieldNumber()) {
case (int) BackgroundInstalledPackageProto.PACKAGE_NAME:
- packageName = protoInputStream.readString(
- BackgroundInstalledPackageProto.PACKAGE_NAME);
+ packageName =
+ protoInputStream.readString(
+ BackgroundInstalledPackageProto.PACKAGE_NAME);
break;
case (int) BackgroundInstalledPackageProto.USER_ID:
- userId = protoInputStream.readInt(
- BackgroundInstalledPackageProto.USER_ID) - 1;
+ userId =
+ protoInputStream.readInt(
+ BackgroundInstalledPackageProto.USER_ID)
+ - 1;
break;
default:
- fail("Undefined field in proto: "
- + protoInputStream.getFieldNumber());
+ fail("Undefined field in proto: " + protoInputStream.getFieldNumber());
}
}
protoInputStream.end(token);
@@ -353,7 +356,7 @@
// Read the file on the disk to verify
var packagesInDisk = new SparseSetArray<>();
AtomicFile atomicFile = new AtomicFile(mFile);
- try (FileInputStream fileInputStream = atomicFile.openRead()) {
+ try (FileInputStream fileInputStream = atomicFile.openRead()) {
ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream);
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
@@ -361,23 +364,25 @@
!= (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) {
continue;
}
- long token = protoInputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token =
+ protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
String packageName = null;
int userId = UserHandle.USER_NULL;
while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (protoInputStream.getFieldNumber()) {
case (int) BackgroundInstalledPackageProto.PACKAGE_NAME:
- packageName = protoInputStream.readString(
- BackgroundInstalledPackageProto.PACKAGE_NAME);
+ packageName =
+ protoInputStream.readString(
+ BackgroundInstalledPackageProto.PACKAGE_NAME);
break;
case (int) BackgroundInstalledPackageProto.USER_ID:
- userId = protoInputStream.readInt(
- BackgroundInstalledPackageProto.USER_ID) - 1;
+ userId =
+ protoInputStream.readInt(
+ BackgroundInstalledPackageProto.USER_ID)
+ - 1;
break;
default:
- fail("Undefined field in proto: "
- + protoInputStream.getFieldNumber());
+ fail("Undefined field in proto: " + protoInputStream.getFieldNumber());
}
}
protoInputStream.end(token);
@@ -399,51 +404,55 @@
@Test
public void testHandleUsageEvent_permissionDenied() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_DENIED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, 0);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0);
mTestLooper.dispatchAll();
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
}
@Test
public void testHandleUsageEvent_permissionGranted() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, 0);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0);
mTestLooper.dispatchAll();
- assertEquals(1,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ assertEquals(
+ 1, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
}
@Test
public void testHandleUsageEvent_ignoredEvent() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.USER_INTERACTION,
- USER_ID_1, INSTALLER_NAME_1, 0);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0);
mTestLooper.dispatchAll();
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
}
@Test
public void testHandleUsageEvent_firstActivityResumedHalfTimeFrame() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
mTestLooper.dispatchAll();
var installerForegroundTimeFrames =
@@ -461,14 +470,18 @@
@Test
public void testHandleUsageEvent_firstActivityResumedOneTimeFrame() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
mTestLooper.dispatchAll();
var installerForegroundTimeFrames =
@@ -486,16 +499,23 @@
@Test
public void testHandleUsageEvent_firstActivityResumedOneAndHalfTimeFrame() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_3);
mTestLooper.dispatchAll();
var installerForegroundTimeFrames =
@@ -517,12 +537,13 @@
@Test
public void testHandleUsageEvent_firstNoneActivityResumed() {
- assertEquals(0,
- mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
+ assertEquals(
+ 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps());
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
mTestLooper.dispatchAll();
var installerForegroundTimeFrames =
@@ -535,27 +556,26 @@
}
@Test
- public void testHandleUsageEvent_packageAddedNoUsageEvent() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedNoUsageEvent()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ INSTALLER_NAME_1,
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -572,27 +592,26 @@
}
@Test
- public void testHandleUsageEvent_packageAddedInsideTimeFrame() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedInsideTimeFrame()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ INSTALLER_NAME_1,
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -604,12 +623,16 @@
// The 2 usage events make the package adding inside a time frame.
// So it's not a background install. Thus, it's null for the return of
// mBackgroundInstallControlService.getBackgroundInstalledPackages()
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
mTestLooper.dispatchAll();
@@ -617,27 +640,26 @@
}
@Test
- public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ INSTALLER_NAME_1,
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -650,12 +672,16 @@
// Compared to testHandleUsageEvent_packageAddedInsideTimeFrame,
// it's a background install. Thus, it's not null for the return of
// mBackgroundInstallControlService.getBackgroundInstalledPackages()
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_2);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
mTestLooper.dispatchAll();
@@ -665,28 +691,28 @@
assertEquals(1, packages.size());
assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
}
+
@Test
- public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedOutsideTimeFrame2()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ INSTALLER_NAME_1,
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -700,12 +726,16 @@
// Compared to testHandleUsageEvent_packageAddedInsideTimeFrame,
// it's a background install. Thus, it's not null for the return of
// mBackgroundInstallControlService.getBackgroundInstalledPackages()
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_2);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3);
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_2,
+ INSTALLER_NAME_2,
+ USAGE_EVENT_TIMESTAMP_2);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3);
mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
mTestLooper.dispatchAll();
@@ -715,31 +745,31 @@
assertEquals(1, packages.size());
assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
}
+
@Test
- public void testHandleUsageEvent_packageAddedThroughAdb() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedThroughAdb()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
// This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the
// initiatingPackageName used to be null but is now "com.android.shell". This test ensures
// that the behavior is still the same for when the initiatingPackageName is null.
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ null,
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ null,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
// b/265203007
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -751,12 +781,16 @@
// for ADB installs the initiatingPackageName used to be null, despite being detected
// as a background install. Since we do not want to treat side-loaded apps as background
// install getBackgroundInstalledPackages() is expected to return null
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_2);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
mTestLooper.dispatchAll();
@@ -764,31 +798,31 @@
var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
assertNull(packages);
}
+
@Test
- public void testHandleUsageEvent_packageAddedThroughAdb2() throws
- NoSuchFieldException, PackageManager.NameNotFoundException {
+ public void testHandleUsageEvent_packageAddedThroughAdb2()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
// This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the
// initiatingPackageName used to be null but is now "com.android.shell". This test ensures
// that the behavior is still the same after this change.
- InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- /* initiatingPackageName = */ "com.android.shell",
- /* initiatingPackageSigningInfo = */ null,
- /* originatingPackageName = */ null,
- /* installingPackageName = */ INSTALLER_NAME_1);
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ "com.android.shell",
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
// b/265203007
when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfoAsUser(
- eq(PACKAGE_NAME_1),
- any(),
- anyInt())
- ).thenReturn(appInfo);
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
- long createTimestamp = PACKAGE_ADD_TIMESTAMP_1
- - (System.currentTimeMillis() - SystemClock.uptimeMillis());
- FieldSetter.setField(appInfo,
+ long createTimestamp =
+ PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ FieldSetter.setField(
+ appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
createTimestamp);
@@ -800,12 +834,16 @@
// for ADB installs the initiatingPackageName is com.android.shell, despite being detected
// as a background install. Since we do not want to treat side-loaded apps as background
// install getBackgroundInstalledPackages() is expected to return null
- doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission(
- anyString(), anyString(), anyInt(), anyInt());
- generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
- generateUsageEvent(Event.ACTIVITY_STOPPED,
- USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyInt(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_2);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3);
mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
mTestLooper.dispatchAll();
@@ -813,6 +851,7 @@
var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
assertNull(packages);
}
+
@Test
public void testPackageRemoved() {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
@@ -859,8 +898,7 @@
packages.add(packageInfo2);
var packageInfo3 = makePackageInfo(PACKAGE_NAME_3);
packages.add(packageInfo3);
- doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(
- any(), anyInt());
+ doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(any(), anyInt());
var resultPackages =
mBackgroundInstallControlService.getBackgroundInstalledPackages(0L, USER_ID_1);
@@ -870,18 +908,44 @@
assertFalse(resultPackages.getList().contains(packageInfo3));
}
+ @Test(expected = SecurityException.class)
+ public void enforceCallerQueryPackagesPermissionsThrowsSecurityException() {
+ doThrow(new SecurityException("test")).when(mContext)
+ .enforceCallingPermission(eq(QUERY_ALL_PACKAGES), anyString());
+
+ mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions();
+ }
+
+ @Test
+ public void enforceCallerQueryPackagesPermissionsDoesNotThrowSecurityException() {
+ //enforceCallerQueryPackagesPermissions do not throw
+
+ mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void enforceCallerInteractCrossUserPermissionsThrowsSecurityException() {
+ doThrow(new SecurityException("test")).when(mContext)
+ .enforceCallingPermission(eq(INTERACT_ACROSS_USERS_FULL), anyString());
+
+ mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions();
+ }
+ @Test
+ public void enforceCallerInteractCrossUserPermissionsDoesNotThrowSecurityException() {
+ //enforceCallerQueryPackagesPermissions do not throw
+
+ mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions();
+ }
+
/**
* Mock a usage event occurring.
*
* @param usageEventId id of a usage event
- * @param userId user id of a usage event
- * @param pkgName package name of a usage event
- * @param timestamp timestamp of a usage event
+ * @param userId user id of a usage event
+ * @param pkgName package name of a usage event
+ * @param timestamp timestamp of a usage event
*/
- private void generateUsageEvent(int usageEventId,
- int userId,
- String pkgName,
- long timestamp) {
+ private void generateUsageEvent(int usageEventId, int userId, String pkgName, long timestamp) {
Event event = new Event(usageEventId, timestamp);
event.mPackage = pkgName;
mUsageEventListener.onUsageEvent(userId, event);
@@ -935,5 +999,10 @@
public File getDiskFile() {
return mFile;
}
+
+ @Override
+ public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() {
+ return mCallbackHelper;
+ }
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index c10c3c2..9b25f58 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -183,7 +183,6 @@
assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse();
verify(mDb, times(2)).disableHistory();
}
-
@Test
public void testAddProfile_historyEnabledInPrimary() {
// create a history
@@ -610,4 +609,14 @@
assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isFalse();
}
+ @Test
+ public void testDelayedPackageRemoval_userLocked() {
+ String pkg = "pkg";
+ mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ mHistoryManager.onUserStopped(USER_SYSTEM);
+ mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
+
+ // no exception, yay
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
index 8773cab..1df7012 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
@@ -24,6 +24,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.flags.Flags;
import java.util.ArrayList;
import java.util.List;
@@ -117,12 +118,28 @@
private boolean checkCallStatus() {
List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList();
if (infoList == null) return false;
- return infoList.stream()
- .filter(s -> (s.getSubscriptionId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
- .anyMatch(s -> isCallOngoingFromState(
- mTelephonyManager
- .createForSubscriptionId(s.getSubscriptionId())
- .getCallStateForSubscription()));
+ if (!Flags.enforceTelephonyFeatureMapping()) {
+ return infoList.stream()
+ .filter(s -> (s.getSubscriptionId()
+ != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+ .anyMatch(s -> isCallOngoingFromState(
+ mTelephonyManager
+ .createForSubscriptionId(s.getSubscriptionId())
+ .getCallStateForSubscription()));
+ } else {
+ return infoList.stream()
+ .filter(s -> (s.getSubscriptionId()
+ != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+ .anyMatch(s -> {
+ try {
+ return isCallOngoingFromState(mTelephonyManager
+ .createForSubscriptionId(s.getSubscriptionId())
+ .getCallStateForSubscription());
+ } catch (UnsupportedOperationException e) {
+ return false;
+ }
+ });
+ }
}
private void updateTelephonyListeners() {
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 250c3a5..2a6ac98 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -196,7 +196,7 @@
// We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+ return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
}
@@ -249,7 +249,7 @@
// We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
+ return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
}
@@ -521,7 +521,7 @@
// We have READ_CALL_LOG permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- return appOps.noteOp(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage,
+ return appOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_CALL_LOG, uid, callingPackage,
callingPackageName, null) == AppOpsManager.MODE_ALLOWED;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c7b84a3..79e4ad0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10955,6 +10955,9 @@
* @return A {@link PersistableBundle} containing the config for the given subId, or default
* values for an invalid subId.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ *
* @deprecated Use {@link #getConfigForSubId(int, String...)} instead.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -11002,6 +11005,9 @@
* @return A {@link PersistableBundle} with key/value mapping for the specified configuration
* on success, or an empty (but never null) bundle on failure (for example, when the calling app
* has no permission).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresPermission(anyOf = {
Manifest.permission.READ_PHONE_STATE,
@@ -11047,6 +11053,9 @@
* @param overrideValues Key-value pairs of the values that are to be overridden. If set to
* {@code null}, this will remove all previous overrides and set the
* carrier configuration back to production values.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -11104,6 +11113,10 @@
*
* @see #getConfigForSubId
* @see #getConfig(String...)
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ *
* @deprecated use {@link #getConfig(String...)} instead.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -11138,6 +11151,9 @@
* configs on success, or an empty (but never null) bundle on failure.
* @see #getConfigForSubId(int, String...)
* @see SubscriptionManager#getDefaultSubscriptionId()
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresPermission(anyOf = {
Manifest.permission.READ_PHONE_STATE,
@@ -11189,6 +11205,9 @@
*
* <p>This method returns before the reload has completed, and {@link
* android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -11212,6 +11231,8 @@
* <p>Depending on simState, the config may be cleared or loaded from config app. This is only
* used by SubscriptionInfoUpdater.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -11234,6 +11255,8 @@
* Gets the package name for a default carrier service.
* @return the package name for a default carrier service; empty string if not available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@NonNull
@@ -11287,6 +11310,9 @@
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
*
* @see #getConfigForSubId
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1b47dfe..0dce084 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -18923,6 +18923,62 @@
}
/**
+ * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+ * are in use by the cellular modem.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+ * and integrity algorithms in use
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public void setEnableNullCipherNotifications(boolean enable) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setEnableNullCipherNotifications(enable);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+ * cellular modem.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+ * and integrity algorithms in use
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public boolean isNullCipherNotificationsEnabled() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isNullCipherNotificationsEnabled();
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+
+ /**
* Get current cell broadcast message identifier ranges.
*
* @throws SecurityException if the caller does not have the required permission
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 9dd83d1..b6f9e1f 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -451,7 +451,7 @@
/**
* Return the network validation status that was initiated by {@link
- * DataService.DataServiceProvider#requestValidation}
+ * DataService.DataServiceProvider#requestNetworkValidation}
*
* @return The network validation status of data connection.
*/
@@ -931,7 +931,7 @@
/**
* Set the network validation status that corresponds to the state of the network validation
- * request started by {@link DataService.DataServiceProvider#requestValidation}
+ * request started by {@link DataService.DataServiceProvider#requestNetworkValidation}
*
* @param status The network validation status.
* @return The same instance of the builder.
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 80e91a3..f04e1c9 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -415,13 +415,13 @@
* request validation to the DataService and checks if the request has been submitted.
*/
@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
- public void requestValidation(int cid,
+ public void requestNetworkValidation(int cid,
@NonNull @CallbackExecutor Executor executor,
@NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
- Log.d(TAG, "requestValidation: " + cid);
+ Log.d(TAG, "requestNetworkValidation: " + cid);
// The default implementation is to return unsupported.
executor.execute(() -> resultCodeCallback
@@ -741,7 +741,7 @@
case DATA_SERVICE_REQUEST_VALIDATION:
if (serviceProvider == null) break;
ValidationRequest validationRequest = (ValidationRequest) message.obj;
- serviceProvider.requestValidation(
+ serviceProvider.requestNetworkValidation(
validationRequest.cid,
validationRequest.executor,
FunctionalUtils
@@ -924,9 +924,10 @@
}
@Override
- public void requestValidation(int slotIndex, int cid, IIntegerConsumer resultCodeCallback) {
+ public void requestNetworkValidation(int slotIndex, int cid,
+ IIntegerConsumer resultCodeCallback) {
if (resultCodeCallback == null) {
- loge("requestValidation: resultCodeCallback is null");
+ loge("requestNetworkValidation: resultCodeCallback is null");
return;
}
ValidationRequest validationRequest =
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 15f8881..c36c302 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -48,5 +48,5 @@
void cancelHandover(int slotId, int cid, IDataServiceCallback callback);
void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
- void requestValidation(int slotId, int cid, IIntegerConsumer callback);
+ void requestNetworkValidation(int slotId, int cid, IIntegerConsumer callback);
}
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 54ceaed..9f83da9 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -84,7 +84,7 @@
SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
- SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK
+ SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS
})
@Retention(RetentionPolicy.SOURCE)
public @interface SuggestedAction {}
@@ -116,9 +116,10 @@
/**
* Indicates that the IMS registration on current RAT failed multiple times.
- * The radio shall block the current RAT and search for other available RATs in the
- * background. If no other RAT is available that meets the carrier requirements, the
- * radio may remain on the current RAT for internet service. The radio clears all
+ * The radio shall block the {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ * included with this and search for other available RATs in the background.
+ * If no other RAT is available that meets the carrier requirements, the
+ * radio may remain on the blocked RAT for internet service. The radio clears all
* RATs marked as unavailable if the IMS service is registered to the carrier network.
* @hide
*/
@@ -133,7 +134,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
- int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK = 4;
+ int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4;
/**@hide*/
// Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java
index 8842886..d6440fc 100644
--- a/telephony/java/android/telephony/satellite/AntennaPosition.java
+++ b/telephony/java/android/telephony/satellite/AntennaPosition.java
@@ -35,10 +35,10 @@
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class AntennaPosition implements Parcelable {
/** Antenna direction used for satellite communication. */
- @NonNull AntennaDirection mAntennaDirection;
+ @NonNull private AntennaDirection mAntennaDirection;
/** Enum corresponding to device hold position to be used by the end user. */
- @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition;
+ @SatelliteManager.DeviceHoldPosition private int mSuggestedHoldPosition;
/**
* @hide
diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
similarity index 94%
rename from telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
rename to telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
index cd9d81e..9ff73e2 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
@@ -20,7 +20,7 @@
* Interface for satellite state change callback.
* @hide
*/
-oneway interface ISatelliteStateCallback {
+oneway interface ISatelliteModemStateCallback {
/**
* Indicates that the satellite modem state has changed.
*
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index 022a856..9440b65 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -108,11 +109,19 @@
return sb.toString();
}
+ /**
+ * Returns the azimuth of the satellite, in degrees.
+ */
+ @FloatRange(from = -180, to = 180)
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getSatelliteAzimuthDegrees() {
return mSatelliteAzimuthDegrees;
}
+ /**
+ * Returns the elevation of the satellite, in degrees.
+ */
+ @FloatRange(from = -90, to = 90)
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getSatelliteElevationDegrees() {
return mSatelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index b5763c3..8e79ca5 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -22,10 +22,15 @@
import com.android.internal.telephony.flags.Flags;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* A callback class for listening to satellite datagrams.
+ * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes
+ * {@link SatelliteManager#registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)},
+ * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked
+ * when a new datagram is received from satellite.
*
* @hide
*/
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index e09bd20..2a69703 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -75,8 +75,9 @@
private static final ConcurrentHashMap<SatelliteProvisionStateCallback,
ISatelliteProvisionStateCallback> sSatelliteProvisionStateCallbackMap =
new ConcurrentHashMap<>();
- private static final ConcurrentHashMap<SatelliteStateCallback, ISatelliteStateCallback>
- sSatelliteStateCallbackMap = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<SatelliteModemStateCallback,
+ ISatelliteModemStateCallback>
+ sSatelliteModemStateCallbackMap = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback,
ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap =
new ConcurrentHashMap<>();
@@ -624,6 +625,11 @@
/**
* Request to get whether the satellite service is supported on the device.
*
+ * <p>
+ * Note: This API only checks whether the device supports the satellite feature. The result will
+ * not be affected by whether the device is provisioned.
+ * </p>
+ *
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1301,21 +1307,22 @@
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@SatelliteResult public int registerForSatelliteModemStateChanged(
@NonNull @CallbackExecutor Executor executor,
- @NonNull SatelliteStateCallback callback) {
+ @NonNull SatelliteModemStateCallback callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- ISatelliteStateCallback internalCallback = new ISatelliteStateCallback.Stub() {
+ ISatelliteModemStateCallback internalCallback =
+ new ISatelliteModemStateCallback.Stub() {
@Override
public void onSatelliteModemStateChanged(int state) {
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onSatelliteModemStateChanged(state)));
}
};
- sSatelliteStateCallbackMap.put(callback, internalCallback);
+ sSatelliteModemStateCallbackMap.put(callback, internalCallback);
return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
} else {
throw new IllegalStateException("telephony service is null.");
@@ -1332,16 +1339,18 @@
* If callback was not registered before, the request will be ignored.
*
* @param callback The callback that was passed to
- * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteStateCallback)}.
+ * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteModemStateCallback)}.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
- public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
+ public void unregisterForSatelliteModemStateChanged(
+ @NonNull SatelliteModemStateCallback callback) {
Objects.requireNonNull(callback);
- ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
+ ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove(
+ callback);
try {
ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
similarity index 96%
rename from telephony/java/android/telephony/satellite/SatelliteStateCallback.java
rename to telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
index bfe6e10..8d33c88 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
@@ -28,7 +28,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-public interface SatelliteStateCallback {
+public interface SatelliteModemStateCallback {
/**
* Called when satellite modem state changes.
* @param state The new satellite modem state.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9b5ee0c..3ea86c7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -72,7 +72,7 @@
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
-import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.ISatelliteModemStateCallback;
import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
@@ -2896,7 +2896,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- int registerForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback);
+ int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
/**
* Unregisters for modem state changed from satellite modem.
@@ -2907,7 +2907,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void unregisterForSatelliteModemStateChanged(int subId, ISatelliteStateCallback callback);
+ void unregisterForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
/**
* Register to receive incoming datagrams over satellite.
@@ -3230,4 +3230,32 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
boolean isCellularIdentifierDisclosureNotificationsEnabled();
+
+ /**
+ * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+ * are in use by the cellular modem.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+ * and integrity algorithms in use
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.MODIFY_PHONE_STATE)")
+ void setEnableNullCipherNotifications(boolean enable);
+
+ /**
+ * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+ * cellular modem.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available
+ * @throws SecurityException if the caller does not have the required privileges
+ * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+ * and integrity algorithms in use
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+ boolean isNullCipherNotificationsEnabled();
}
diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
index 2bc056e..fee1b25 100644
--- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
+++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
@@ -16,6 +16,9 @@
package android.transparency.test.app;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -27,6 +30,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.internal.os.IBinaryTransparencyService.AppInfo;
import org.junit.Before;
@@ -116,7 +120,13 @@
@Test
public void testCollectAllSilentInstalledMbaInfo() {
// Action
- var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle());
+ var appInfoList =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mBt,
+ (Bt) ->
+ mBt.collectAllSilentInstalledMbaInfo(new Bundle()),
+ QUERY_ALL_PACKAGES,
+ INTERACT_ACROSS_USERS_FULL);
// Verify
assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 80c1e5be..217659e 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -43,9 +43,7 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -178,8 +176,14 @@
this.deviceFrameRate = deviceFrameRate;
}
+ FrameRateTimeoutException(List<Float> expectedFrameRates, float deviceFrameRate) {
+ this.expectedFrameRates = expectedFrameRates;
+ this.deviceFrameRate = deviceFrameRate;
+ }
+
public float expectedFrameRate;
public float deviceFrameRate;
+ public List<Float> expectedFrameRates;
}
public enum Api {
@@ -502,31 +506,37 @@
}
private void waitForStableFrameRate(TestSurface... surfaces) throws InterruptedException {
- verifyCompatibleAndStableFrameRate(0, surfaces);
+ verifyFrameRates(List.of(), surfaces);
}
private void verifyExactAndStableFrameRate(
float expectedFrameRate,
TestSurface... surfaces) throws InterruptedException {
- verifyFrameRate(expectedFrameRate, false, surfaces);
+ verifyFrameRate(List.of(expectedFrameRate), false, surfaces);
}
private void verifyCompatibleAndStableFrameRate(
float expectedFrameRate,
TestSurface... surfaces) throws InterruptedException {
- verifyFrameRate(expectedFrameRate, true, surfaces);
+ verifyFrameRate(List.of(expectedFrameRate), true, surfaces);
}
- // Set expectedFrameRate to 0.0 to verify only stable frame rate.
- private void verifyFrameRate(
- float expectedFrameRate, boolean multiplesAllowed,
+ /** Verify stable frame rate at one of the expectedFrameRates. */
+ private void verifyFrameRates(List<Float> expectedFrameRates, TestSurface... surfaces)
+ throws InterruptedException {
+ verifyFrameRate(expectedFrameRates, true, surfaces);
+ }
+
+ // Set expectedFrameRates to empty to verify only stable frame rate.
+ private void verifyFrameRate(List<Float> expectedFrameRates, boolean multiplesAllowed,
TestSurface... surfaces) throws InterruptedException {
Log.i(TAG, "Verifying compatible and stable frame rate");
long nowNanos = System.nanoTime();
long gracePeriodEndTimeNanos =
nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
while (true) {
- if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
+ if (expectedFrameRates.size() == 1) {
+ float expectedFrameRate = expectedFrameRates.get(0);
// Wait until we switch to a compatible frame rate.
Log.i(TAG,
String.format(
@@ -557,11 +567,25 @@
while (endTimeNanos > nowNanos) {
int numModeChangedEvents = mModeChangedEvents.size();
if (waitForEvents(endTimeNanos, surfaces)) {
- Log.i(TAG,
- String.format("Stable frame rate %.2f verified",
- multiplesAllowed ? mDisplayModeRefreshRate
- : mDisplayRefreshRate));
- return;
+ // Verify any expected frame rate since there are multiple that will suffice.
+ // Mainly to account for running tests on real devices, where other non-test
+ // layers may affect the outcome.
+ if (expectedFrameRates.size() > 1) {
+ for (float expectedFrameRate : expectedFrameRates) {
+ if (isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)) {
+ return;
+ }
+ }
+ // The frame rate is stable but it is not one of the expected frame rates.
+ throw new FrameRateTimeoutException(
+ expectedFrameRates, mDisplayModeRefreshRate);
+ } else {
+ Log.i(TAG,
+ String.format("Stable frame rate %.2f verified",
+ multiplesAllowed ? mDisplayModeRefreshRate
+ : mDisplayRefreshRate));
+ return;
+ }
}
nowNanos = System.nanoTime();
if (mModeChangedEvents.size() > numModeChangedEvents) {
@@ -623,12 +647,28 @@
// caused the timeout failure. Wait for a bit to see if we get notified
// of a precondition violation, and if so, retry the test. Otherwise
// fail.
- assertTrue(String.format(
- "Timed out waiting for a stable and compatible frame"
- + " rate. expected=%.2f received=%.2f."
- + " Stack trace: " + stackTrace,
- exc.expectedFrameRate, exc.deviceFrameRate),
- waitForPreconditionViolation());
+ if (exc.expectedFrameRates.isEmpty()) {
+ assertTrue(
+ String.format(
+ "Timed out waiting for a stable and compatible"
+ + " frame rate."
+ + " expected=%.2f received=%.2f."
+ + " Stack trace: " + stackTrace,
+ exc.expectedFrameRate, exc.deviceFrameRate),
+ waitForPreconditionViolation());
+ } else {
+ assertTrue(
+ String.format(
+ "Timed out waiting for a stable and compatible"
+ + " frame rate."
+ + " expected={%.2f} received=%.2f."
+ + " Stack trace: " + stackTrace,
+ exc.expectedFrameRates.stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", ")),
+ exc.deviceFrameRate),
+ waitForPreconditionViolation());
+ }
}
if (!testPassed) {
@@ -679,10 +719,15 @@
"**** Running testSurfaceControlFrameRateCompatibility with compatibility "
+ compatibility);
- float expectedFrameRate = getExpectedFrameRateForCompatibility(compatibility);
+ List<Float> expectedFrameRates = getExpectedFrameRateForCompatibility(compatibility);
+ Log.i(TAG,
+ "Expected frame rates: "
+ + expectedFrameRates.stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", ")));
int initialNumEvents = mModeChangedEvents.size();
surface.setFrameRate(30.f, compatibility);
- verifyExactAndStableFrameRate(expectedFrameRate, surface);
+ verifyFrameRates(expectedFrameRates, surface);
verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
});
}
@@ -699,10 +744,10 @@
runOneSurfaceTest((TestSurface surface) -> {
Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category);
- float expectedFrameRate = getExpectedFrameRateForCategory(category);
+ List<Float> expectedFrameRates = getExpectedFrameRateForCategory(category);
int initialNumEvents = mModeChangedEvents.size();
surface.setFrameRateCategory(category);
- verifyCompatibleAndStableFrameRate(expectedFrameRate, surface);
+ verifyFrameRates(expectedFrameRates, surface);
verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
});
}
@@ -771,41 +816,41 @@
"frame rate strategy=" + parentStrategy);
}
- private float getExpectedFrameRateForCompatibility(int compatibility) {
+ private List<Float> getExpectedFrameRateForCompatibility(int compatibility) {
assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility "
+ compatibility,
compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE);
Display display = getDisplay();
- Optional<Float> expectedFrameRate = getRefreshRates(display.getMode(), display)
- .stream()
- .filter(rate -> rate >= 30.f)
- .min(Comparator.naturalOrder());
+ List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display)
+ .stream()
+ .filter(rate -> rate >= 30.f)
+ .collect(Collectors.toList());
assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED because no refresh rate "
+ "is >= 30",
- expectedFrameRate.isPresent());
- return expectedFrameRate.get();
+ !expectedFrameRates.isEmpty());
+ return expectedFrameRates;
}
- private float getExpectedFrameRateForCategory(int category) {
+ private List<Float> getExpectedFrameRateForCategory(int category) {
Display display = getDisplay();
List<Float> frameRates = getRefreshRates(display.getMode(), display);
if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) {
// Max due to default vote and no other frame rate specifications.
- return Collections.max(frameRates);
+ return List.of(Collections.max(frameRates));
} else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) {
- return Collections.min(frameRates);
+ return frameRates;
}
FpsRange categoryRange = convertCategory(category);
- Optional<Float> expectedFrameRate = frameRates.stream()
- .filter(fps -> categoryRange.includes(fps))
- .min(Comparator.naturalOrder());
+ List<Float> expectedFrameRates = frameRates.stream()
+ .filter(fps -> categoryRange.includes(fps))
+ .collect(Collectors.toList());
assumeTrue("**** testSurfaceControlFrameRateCategory SKIPPED for category " + category,
- expectedFrameRate.isPresent());
- return expectedFrameRate.get();
+ !expectedFrameRates.isEmpty());
+ return expectedFrameRates;
}
private FpsRange convertCategory(int category) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index 29cbf01..e3988cd 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -24,8 +24,8 @@
import android.view.View;
import android.widget.ToggleButton;
+import androidx.window.embedding.ActivityEmbeddingController;
import androidx.window.embedding.SplitAttributes;
-import androidx.window.embedding.SplitAttributesCalculatorParams;
import androidx.window.embedding.SplitController;
/**
@@ -66,7 +66,9 @@
@Override
public void onClick(View v) {
// This triggers a recalcuation of splitatributes.
- mSplitController.invalidateTopVisibleSplitAttributes();
+ ActivityEmbeddingController
+ .getInstance(ActivityEmbeddingSecondaryActivity.this)
+ .invalidateTopVisibleActivityStacks();
}
});
findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
index be479f2..1b02792 100644
--- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
+++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -25,6 +25,7 @@
import android.security.Flags;
import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -52,7 +53,6 @@
private static final String TARGET_PACKAGE = "com.android.fsverity";
private static final String BASENAME = "test.file";
- private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME;
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -63,11 +63,11 @@
prepareTest(10000);
ITestDevice device = getDevice();
- BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0);
- BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 0);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192);
BlockDeviceWriter.dropCaches(device);
- verifyRead(TARGET_PATH, "0,2");
+ verifyRead(getTargetFilePath(), "0,2");
}
@Test
@@ -75,12 +75,17 @@
prepareTest(128 * 4096 + 1);
ITestDevice device = getDevice();
- BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096);
- BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096);
- BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 100 * 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1);
BlockDeviceWriter.dropCaches(device);
- verifyRead(TARGET_PATH, "1,100,128");
+ verifyRead(getTargetFilePath(), "1,100,128");
+ }
+
+ private String getTargetFilePath() throws DeviceNotAvailableException {
+ return "/data/user/" + getDevice().getCurrentUser() + "/" + TARGET_PACKAGE + "/files/"
+ + BASENAME;
}
private void prepareTest(int fileSize) throws Exception {
@@ -97,7 +102,7 @@
options.setTestClassName(TARGET_PACKAGE + ".Helper");
options.setTestMethodName("verifyFileRead");
options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
- options.addInstrumentationArg("filePath", TARGET_PATH);
+ options.addInstrumentationArg("filePath", getTargetFilePath());
assertThat(runDeviceTests(options)).isTrue();
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 692c8a8..49665f7 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -27,15 +27,18 @@
import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
+import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -55,6 +58,7 @@
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnManager;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.os.ParcelUuid;
@@ -81,6 +85,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@@ -352,4 +357,71 @@
any(Executor.class),
any(ConnectivityDiagnosticsCallback.class));
}
+
+ private void verifyGetSafeModeTimeoutMs(
+ boolean isInTestMode,
+ boolean isConfigTimeoutSupported,
+ PersistableBundleWrapper carrierConfig,
+ long expectedTimeoutMs)
+ throws Exception {
+ doReturn(isInTestMode).when(mVcnContext).isInTestMode();
+ doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
+
+ final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
+ doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP);
+
+ final long result =
+ VcnGatewayConnection.getSafeModeTimeoutMs(mVcnContext, snapshot, TEST_SUB_GRP);
+
+ assertEquals(expectedTimeoutMs, result);
+ }
+
+ @Test
+ public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception {
+ verifyGetSafeModeTimeoutMs(
+ false /* isInTestMode */,
+ false /* isConfigTimeoutSupported */,
+ null /* carrierConfig */,
+ TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+ }
+
+ @Test
+ public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception {
+ final int carrierConfigTimeoutSeconds = 20;
+ final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class);
+ doReturn(carrierConfigTimeoutSeconds)
+ .when(carrierConfig)
+ .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt());
+
+ verifyGetSafeModeTimeoutMs(
+ false /* isInTestMode */,
+ true /* isConfigTimeoutSupported */,
+ carrierConfig,
+ TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
+ }
+
+ @Test
+ public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull()
+ throws Exception {
+ verifyGetSafeModeTimeoutMs(
+ false /* isInTestMode */,
+ true /* isConfigTimeoutSupported */,
+ null /* carrierConfig */,
+ TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+ }
+
+ @Test
+ public void testGetSafeModeTimeoutMs_configTimeoutOverrideTestModeDefault() throws Exception {
+ final int carrierConfigTimeoutSeconds = 20;
+ final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class);
+ doReturn(carrierConfigTimeoutSeconds)
+ .when(carrierConfig)
+ .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt());
+
+ verifyGetSafeModeTimeoutMs(
+ true /* isInTestMode */,
+ true /* isConfigTimeoutSupported */,
+ carrierConfig,
+ TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds));
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index edced87..4c7b25a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -67,6 +67,8 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock;
import com.android.server.vcn.routeselection.UnderlyingNetworkController;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
@@ -118,13 +120,7 @@
NetworkCapabilities networkCapabilities,
LinkProperties linkProperties,
boolean isBlocked) {
- return new UnderlyingNetworkRecord(
- network,
- networkCapabilities,
- linkProperties,
- isBlocked,
- false /* isSelected */,
- 0 /* priorityClass */);
+ return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked);
}
protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4";
@@ -226,6 +222,7 @@
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
+ doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
new file mode 100644
index 0000000..bf84bbe
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -0,0 +1,121 @@
+/*
+ * 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.vcn.routeselection;
+
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.TelephonyManager;
+
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
+
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+import java.util.UUID;
+
+public abstract class NetworkEvaluationTestBase {
+ protected static final String SSID = "TestWifi";
+ protected static final String SSID_OTHER = "TestWifiOther";
+ protected static final String PLMN_ID = "123456";
+ protected static final String PLMN_ID_OTHER = "234567";
+
+ protected static final int SUB_ID = 1;
+ protected static final int WIFI_RSSI = -60;
+ protected static final int WIFI_RSSI_HIGH = -50;
+ protected static final int WIFI_RSSI_LOW = -80;
+ protected static final int CARRIER_ID = 1;
+ protected static final int CARRIER_ID_OTHER = 2;
+
+ protected static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+ protected static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+ protected static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+ protected static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
+ protected static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+
+ protected static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .setSignalStrength(WIFI_RSSI)
+ .setSsid(SSID)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+ .build();
+
+ protected static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
+ new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
+ protected static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setSubscriptionIds(Set.of(SUB_ID))
+ .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+ .build();
+
+ protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
+
+ @Mock protected Network mNetwork;
+ @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+ @Mock protected TelephonyManager mTelephonyManager;
+
+ protected TestLooper mTestLooper;
+ protected VcnContext mVcnContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final Context mockContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mVcnContext =
+ spy(
+ new VcnContext(
+ mockContext,
+ mTestLooper.getLooper(),
+ mock(VcnNetworkProvider.class),
+ false /* isInTestMode */));
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+
+ setupSystemService(
+ mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
+ when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 2266041..dbf2f51 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -24,152 +24,48 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
-import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
-import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.net.LinkProperties;
-import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
-import android.os.ParcelUuid;
import android.os.PersistableBundle;
-import android.os.test.TestLooper;
-import android.telephony.TelephonyManager;
import android.util.ArraySet;
-import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.VcnContext;
-import com.android.server.vcn.VcnNetworkProvider;
-
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import java.util.UUID;
-public class NetworkPriorityClassifierTest {
- private static final String SSID = "TestWifi";
- private static final String SSID_OTHER = "TestWifiOther";
- private static final String PLMN_ID = "123456";
- private static final String PLMN_ID_OTHER = "234567";
-
- private static final int SUB_ID = 1;
- private static final int WIFI_RSSI = -60;
- private static final int WIFI_RSSI_HIGH = -50;
- private static final int WIFI_RSSI_LOW = -80;
- private static final int CARRIER_ID = 1;
- private static final int CARRIER_ID_OTHER = 2;
-
- private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
- private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
-
- private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
- private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
-
- private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
-
- private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .setSignalStrength(WIFI_RSSI)
- .setSsid(SSID)
- .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
- .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
- .build();
-
- private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
- new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
- private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
- new NetworkCapabilities.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setSubscriptionIds(Set.of(SUB_ID))
- .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
- .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
- .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
- .build();
-
- private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
-
- @Mock private Network mNetwork;
- @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
- @Mock private TelephonyManager mTelephonyManager;
-
- private TestLooper mTestLooper;
- private VcnContext mVcnContext;
+public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase {
private UnderlyingNetworkRecord mWifiNetworkRecord;
private UnderlyingNetworkRecord mCellNetworkRecord;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ super.setUp();
- final Context mockContext = mock(Context.class);
- mTestLooper = new TestLooper();
- mVcnContext =
- spy(
- new VcnContext(
- mockContext,
- mTestLooper.getLooper(),
- mock(VcnNetworkProvider.class),
- false /* isInTestMode */));
- doNothing().when(mVcnContext).ensureRunningOnLooperThread();
-
- setupSystemService(
- mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
- when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
- when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
- when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
-
- mWifiNetworkRecord =
- getTestNetworkRecord(
- WIFI_NETWORK_CAPABILITIES,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
- mCellNetworkRecord =
- getTestNetworkRecord(
- CELL_NETWORK_CAPABILITIES,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES);
+ mCellNetworkRecord = getTestNetworkRecord(CELL_NETWORK_CAPABILITIES);
}
- private UnderlyingNetworkRecord getTestNetworkRecord(
- NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
- return new UnderlyingNetworkRecord(
- mNetwork,
- nc,
- LINK_PROPERTIES,
- false /* isBlocked */,
- mVcnContext,
- underlyingNetworkTemplates,
- SUB_GROUP,
- mSubscriptionSnapshot,
- null /* currentlySelected */,
- null /* carrierConfig */);
+ private UnderlyingNetworkRecord getTestNetworkRecord(NetworkCapabilities nc) {
+ return new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */);
}
@Test
@@ -186,14 +82,14 @@
mWifiNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot,
- null /* currentlySelecetd */,
+ false /* isSelected */,
null /* carrierConfig */));
}
private void verifyMatchesPriorityRuleForUpstreamBandwidth(
int entryUpstreamBandwidth,
int exitUpstreamBandwidth,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
boolean expectMatch) {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
@@ -208,14 +104,14 @@
mWifiNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot,
- currentlySelected,
+ isSelected,
null /* carrierConfig */));
}
private void verifyMatchesPriorityRuleForDownstreamBandwidth(
int entryDownstreamBandwidth,
int exitDownstreamBandwidth,
- UnderlyingNetworkRecord currentlySelected,
+ boolean isSelected,
boolean expectMatch) {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
@@ -231,7 +127,7 @@
mWifiNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot,
- currentlySelected,
+ isSelected,
null /* carrierConfig */));
}
@@ -240,7 +136,7 @@
verifyMatchesPriorityRuleForUpstreamBandwidth(
TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
- null /* currentlySelected */,
+ false /* isSelected */,
true);
}
@@ -249,7 +145,7 @@
verifyMatchesPriorityRuleForUpstreamBandwidth(
LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
- null /* currentlySelected */,
+ false /* isSelected */,
false);
}
@@ -258,7 +154,7 @@
verifyMatchesPriorityRuleForDownstreamBandwidth(
TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
- null /* currentlySelected */,
+ false /* isSelected */,
true);
}
@@ -267,7 +163,7 @@
verifyMatchesPriorityRuleForDownstreamBandwidth(
LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
- null /* currentlySelected */,
+ false /* isSelected */,
false);
}
@@ -276,7 +172,7 @@
verifyMatchesPriorityRuleForUpstreamBandwidth(
TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
- mWifiNetworkRecord,
+ true /* isSelected */,
true);
}
@@ -285,7 +181,7 @@
verifyMatchesPriorityRuleForUpstreamBandwidth(
LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
- mWifiNetworkRecord,
+ true /* isSelected */,
false);
}
@@ -294,7 +190,7 @@
verifyMatchesPriorityRuleForDownstreamBandwidth(
TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
- mWifiNetworkRecord,
+ true /* isSelected */,
true);
}
@@ -303,7 +199,7 @@
verifyMatchesPriorityRuleForDownstreamBandwidth(
LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
- mWifiNetworkRecord,
+ true /* isSelected */,
false);
}
@@ -318,14 +214,12 @@
TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
- final UnderlyingNetworkRecord selectedNetworkRecord =
- isSelectedNetwork ? mWifiNetworkRecord : null;
assertEquals(
expectMatch,
checkMatchesWifiPriorityRule(
wifiNetworkPriority,
mWifiNetworkRecord,
- selectedNetworkRecord,
+ isSelectedNetwork,
carrierConfig == null
? null
: new PersistableBundleWrapper(carrierConfig)));
@@ -381,7 +275,7 @@
checkMatchesWifiPriorityRule(
wifiNetworkPriority,
mWifiNetworkRecord,
- null /* currentlySelecetd */,
+ false /* isSelected */,
null /* carrierConfig */));
}
@@ -516,7 +410,7 @@
mCellNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot,
- null /* currentlySelected */,
+ false /* isSelected */,
null /* carrierConfig */));
}
@@ -543,7 +437,16 @@
@Test
public void testCalculatePriorityClass() throws Exception {
- assertEquals(2, mCellNetworkRecord.priorityClass);
+ final int priorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ mVcnContext,
+ mCellNetworkRecord,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ false /* isSelected */,
+ null /* carrierConfig */);
+ assertEquals(2, priorityClass);
}
private void checkCalculatePriorityClassFailToMatchAny(
@@ -561,10 +464,19 @@
ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
}
- final UnderlyingNetworkRecord nonDunNetworkRecord =
- getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
+ final UnderlyingNetworkRecord nonDunNetworkRecord = getTestNetworkRecord(ncBuilder.build());
- assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
+ final int priorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ mVcnContext,
+ nonDunNetworkRecord,
+ templatesRequireDun,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ false /* isSelected */,
+ null /* carrierConfig */);
+
+ assertEquals(expectedPriorityClass, priorityClass);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 2941fde..992f102 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -29,13 +29,12 @@
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -67,6 +66,7 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
@@ -77,6 +77,7 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.ArrayList;
import java.util.Arrays;
@@ -154,10 +155,13 @@
@Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
@Mock private Network mNetwork;
+ @Spy private Dependencies mDependencies = new Dependencies();
+
@Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
+ private UnderlyingNetworkEvaluator mNetworkEvaluator;
private UnderlyingNetworkController mUnderlyingNetworkController;
@Before
@@ -189,13 +193,28 @@
when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
+ mNetworkEvaluator =
+ spy(
+ new UnderlyingNetworkEvaluator(
+ mVcnContext,
+ mNetwork,
+ VcnGatewayConnectionConfigTest.buildTestConfig()
+ .getVcnUnderlyingNetworkPriorities(),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null));
+ doReturn(mNetworkEvaluator)
+ .when(mDependencies)
+ .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
+
mUnderlyingNetworkController =
new UnderlyingNetworkController(
mVcnContext,
VcnGatewayConnectionConfigTest.buildTestConfig(),
SUB_GROUP,
mSubscriptionSnapshot,
- mNetworkControllerCb);
+ mNetworkControllerCb,
+ mDependencies);
}
private void resetVcnContext() {
@@ -489,13 +508,7 @@
NetworkCapabilities networkCapabilities,
LinkProperties linkProperties,
boolean isBlocked) {
- return new UnderlyingNetworkRecord(
- network,
- networkCapabilities,
- linkProperties,
- isBlocked,
- false /* isSelected */,
- 0 /* priorityClass */);
+ return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked);
}
@Test
@@ -515,24 +528,12 @@
UnderlyingNetworkRecord recordC =
new UnderlyingNetworkRecord(
mNetwork,
- INITIAL_NETWORK_CAPABILITIES,
- INITIAL_LINK_PROPERTIES,
- false /* isBlocked */,
- true /* isSelected */,
- -1 /* priorityClass */);
- UnderlyingNetworkRecord recordD =
- getTestNetworkRecord(
- mNetwork,
UPDATED_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
assertEquals(recordA, recordB);
- assertEquals(recordA, recordC);
- assertNotEquals(recordA, recordD);
-
- assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB));
- assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC));
+ assertNotEquals(recordA, recordC);
}
@Test
@@ -540,6 +541,19 @@
verifyRegistrationOnAvailableAndGetCallback();
}
+ @Test
+ public void testUpdateSubscriptionSnapshotAndCarrierConfig() {
+ verifyRegistrationOnAvailableAndGetCallback();
+
+ TelephonySubscriptionSnapshot subscriptionUpdate =
+ mock(TelephonySubscriptionSnapshot.class);
+ when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+ mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate);
+
+ verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any());
+ }
+
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
}
@@ -583,6 +597,7 @@
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
+ verify(mNetworkEvaluator).setIsSelected(eq(true), any(), any(), any(), any());
return cb;
}
@@ -713,7 +728,8 @@
VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
SUB_GROUP,
mSubscriptionSnapshot,
- mNetworkControllerCb);
+ mNetworkControllerCb,
+ mDependencies);
verify(cm)
.registerNetworkCallback(
@@ -724,30 +740,43 @@
return mUnderlyingNetworkListenerCaptor.getValue();
}
- private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
+ private UnderlyingNetworkEvaluator bringupNetworkAndGetEvaluator(
UnderlyingNetworkListener cb,
NetworkCapabilities requestNetworkCaps,
- List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
- UnderlyingNetworkRecord currentlySelected) {
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
final Network network = mock(Network.class);
final NetworkCapabilities responseNetworkCaps =
buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
+ final UnderlyingNetworkEvaluator evaluator =
+ spy(
+ new UnderlyingNetworkEvaluator(
+ mVcnContext,
+ network,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null));
+ doReturn(evaluator)
+ .when(mDependencies)
+ .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
cb.onAvailable(network);
cb.onCapabilitiesChanged(network, responseNetworkCaps);
cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
cb.onBlockedStatusChanged(network, false /* isFalse */);
- return new UnderlyingNetworkRecord(
- network,
- responseNetworkCaps,
- INITIAL_LINK_PROPERTIES,
- false /* isBlocked */,
- mVcnContext,
- underlyingNetworkTemplates,
- SUB_GROUP,
- mSubscriptionSnapshot,
- currentlySelected,
- null /* carrierConfig */);
+
+ return evaluator;
+ }
+
+ private void verifySelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) {
+ verifyOnSelectedUnderlyingNetworkChanged(expectedEvaluator.getNetworkRecord());
+ verify(expectedEvaluator).setIsSelected(eq(true), any(), any(), any(), any());
+ }
+
+ private void verifyNeverSelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) {
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(eq(expectedEvaluator.getNetworkRecord()));
+ verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any());
}
@Test
@@ -759,19 +788,15 @@
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up CBS network
- final UnderlyingNetworkRecord cbsNetworkRecord =
- bringupNetworkAndGetRecord(
- cb,
- CBS_NETWORK_CAPABILITIES,
- networkTemplates,
- null /* currentlySelected */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+ final UnderlyingNetworkEvaluator cbsNetworkEvaluator =
+ bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+ verifySelectNetwork(cbsNetworkEvaluator);
// Bring up DUN network
- final UnderlyingNetworkRecord dunNetworkRecord =
- bringupNetworkAndGetRecord(
- cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+ final UnderlyingNetworkEvaluator dunNetworkEvaluator =
+ bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
+ verifySelectNetwork(dunNetworkEvaluator);
+ verify(cbsNetworkEvaluator).setIsSelected(eq(false), any(), any(), any(), any());
}
@Test
@@ -783,20 +808,14 @@
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up DUN network
- final UnderlyingNetworkRecord dunNetworkRecord =
- bringupNetworkAndGetRecord(
- cb,
- DUN_NETWORK_CAPABILITIES,
- networkTemplates,
- null /* currentlySelected */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+ final UnderlyingNetworkEvaluator dunNetworkEvaluator =
+ bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates);
+ verifySelectNetwork(dunNetworkEvaluator);
// Bring up CBS network
- final UnderlyingNetworkRecord cbsNetworkRecord =
- bringupNetworkAndGetRecord(
- cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
- verify(mNetworkControllerCb, never())
- .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+ final UnderlyingNetworkEvaluator cbsNetworkEvaluator =
+ bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+ verifyNeverSelectNetwork(cbsNetworkEvaluator);
}
@Test
@@ -808,13 +827,9 @@
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
// Bring up an Internet network without DUN capability
- final UnderlyingNetworkRecord networkRecord =
- bringupNetworkAndGetRecord(
- cb,
- INITIAL_NETWORK_CAPABILITIES,
- networkTemplates,
- null /* currentlySelected */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
+ final UnderlyingNetworkEvaluator evaluator =
+ bringupNetworkAndGetEvaluator(cb, INITIAL_NETWORK_CAPABILITIES, networkTemplates);
+ verifySelectNetwork(evaluator);
}
@Test
@@ -825,10 +840,8 @@
new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
- bringupNetworkAndGetRecord(
- cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
-
- verify(mNetworkControllerCb, never())
- .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
+ final UnderlyingNetworkEvaluator evaluator =
+ bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates);
+ verifyNeverSelectNetwork(evaluator);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
new file mode 100644
index 0000000..a4567dd
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.vcn.routeselection;
+
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.os.PersistableBundle;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
+ private PersistableBundleWrapper mCarrierConfig;
+
+ @Before
+ public void setUp() {
+ super.setUp();
+ mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle());
+ }
+
+ private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() {
+ return new UnderlyingNetworkEvaluator(
+ mVcnContext,
+ mNetwork,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mCarrierConfig);
+ }
+
+ @Test
+ public void testInitializedEvaluator() throws Exception {
+ final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator();
+
+ assertFalse(evaluator.isValid());
+ assertEquals(mNetwork, evaluator.getNetwork());
+ assertEquals(PRIORITY_INVALID, evaluator.getPriorityClass());
+
+ try {
+ evaluator.getNetworkRecord();
+ fail("Expected to fail because evaluator is not valid");
+ } catch (Exception expected) {
+ }
+ }
+
+ @Test
+ public void testValidEvaluator() {
+ final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator();
+ evaluator.setNetworkCapabilities(
+ CELL_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mCarrierConfig);
+ evaluator.setLinkProperties(
+ LINK_PROPERTIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mCarrierConfig);
+ evaluator.setIsBlocked(
+ false /* isBlocked */,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mCarrierConfig);
+
+ final UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ CELL_NETWORK_CAPABILITIES,
+ LINK_PROPERTIES,
+ false /* isBlocked */);
+
+ assertTrue(evaluator.isValid());
+ assertEquals(mNetwork, evaluator.getNetwork());
+ assertEquals(2, evaluator.getPriorityClass());
+ assertEquals(expectedRecord, evaluator.getNetworkRecord());
+ }
+}