Merge "Revert "Add the Haptics team to RingtonePlayer/NotificationPlayer OWNERS."" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 9ee74e3..1c6df75 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -56,6 +56,7 @@
":android.service.notification.flags-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
":android.speech.flags-aconfig-java{.generated_srcjars}",
+ ":android.systemserver.flags-aconfig-java{.generated_srcjars}",
":android.tracing.flags-aconfig-java{.generated_srcjars}",
":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
@@ -1159,3 +1160,16 @@
host_supported: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// System Server
+aconfig_declarations {
+ name: "android.systemserver.flags-aconfig",
+ package: "android.server",
+ srcs: ["services/java/com/android/server/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.systemserver.flags-aconfig-java",
+ aconfig_declarations: "android.systemserver.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 324d8ca..7284f47 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -23,7 +23,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.util.TimeUtils.formatDuration;
import android.annotation.BytesLong;
@@ -50,9 +49,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.os.Process;
import android.os.Trace;
-import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
@@ -206,6 +203,8 @@
/* Minimum flex for a periodic job, in milliseconds. */
private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
+ private static final long MIN_ALLOWED_TIME_WINDOW_MILLIS = MIN_PERIOD_MILLIS;
+
/**
* Minimum backoff interval for a job, in milliseconds
* @hide
@@ -1881,11 +1880,12 @@
}
/**
- * Set deadline which is the maximum scheduling latency. The job will be run by this
- * deadline even if other requirements (including a delay set through
- * {@link #setMinimumLatency(long)}) are not met.
+ * Set a deadline after which all other functional requested constraints will be ignored.
+ * After the deadline has passed, the job can run even if other requirements (including
+ * a delay set through {@link #setMinimumLatency(long)}) are not met.
* {@link JobParameters#isOverrideDeadlineExpired()} will return {@code true} if the job's
- * deadline has passed.
+ * deadline has passed. The job's execution may be delayed beyond the set deadline by
+ * other factors such as Doze mode and system health signals.
*
* <p>
* Because it doesn't make sense setting this property on a periodic job, doing so will
@@ -1894,30 +1894,23 @@
*
* <p class="note">
* Since a job will run once the deadline has passed regardless of the status of other
- * constraints, setting a deadline of 0 with other constraints makes those constraints
- * meaningless when it comes to execution decisions. Avoid doing this.
- * </p>
- *
- * <p>
- * Short deadlines hinder the system's ability to optimize scheduling behavior and may
- * result in running jobs at inopportune times. Therefore, starting in Android version
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, minimum time windows will be
- * enforced to help make it easier to better optimize job execution. Time windows are
+ * constraints, setting a deadline of 0 (or a {@link #setMinimumLatency(long) delay} equal
+ * to the deadline) with other constraints makes those constraints
+ * meaningless when it comes to execution decisions. Since doing so is indicative of an
+ * error in the logic, starting in Android version
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, jobs with extremely short
+ * time windows will fail to build. Time windows are
* defined as the time between a job's {@link #setMinimumLatency(long) minimum latency}
* and its deadline. If the minimum latency is not set, it is assumed to be 0.
- * The following minimums will be enforced:
- * <ul>
- * <li>
- * Jobs with {@link #PRIORITY_DEFAULT} or higher priorities have a minimum time
- * window of one hour.
- * </li>
- * <li>Jobs with {@link #PRIORITY_LOW} have a minimum time window of 6 hours.</li>
- * <li>Jobs with {@link #PRIORITY_MIN} have a minimum time window of 12 hours.</li>
- * </ul>
*
* Work that must happen immediately should use {@link #setExpedited(boolean)} or
* {@link #setUserInitiated(boolean)} in the appropriate manner.
*
+ * <p>
+ * This API aimed to guarantee execution of the job by the deadline only on Android version
+ * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. That aim and guarantee has not existed
+ * since {@link android.os.Build.VERSION_CODES#M}.
+ *
* @see JobInfo#getMaxExecutionDelayMillis()
*/
public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
@@ -2347,35 +2340,36 @@
throw new IllegalArgumentException("Invalid priority level provided: " + mPriority);
}
- if (enforceMinimumTimeWindows
- && Flags.enforceMinimumTimeWindows()
- // TODO(312197030): remove exemption for the system
- && !UserHandle.isCore(Process.myUid())
- && hasLateConstraint && !isPeriodic) {
- final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
- if (mPriority >= PRIORITY_DEFAULT) {
- if (maxExecutionDelayMillis - windowStart < HOUR_IN_MILLIS) {
- throw new IllegalArgumentException(
- getPriorityString(mPriority)
- + " cannot have a time window less than 1 hour."
- + " Delay=" + windowStart
- + ", deadline=" + maxExecutionDelayMillis);
- }
- } else if (mPriority >= PRIORITY_LOW) {
- if (maxExecutionDelayMillis - windowStart < 6 * HOUR_IN_MILLIS) {
- throw new IllegalArgumentException(
- getPriorityString(mPriority)
- + " cannot have a time window less than 6 hours."
- + " Delay=" + windowStart
- + ", deadline=" + maxExecutionDelayMillis);
- }
+ final boolean hasFunctionalConstraint = networkRequest != null
+ || constraintFlags != 0
+ || (triggerContentUris != null && triggerContentUris.length > 0);
+ if (hasLateConstraint && !isPeriodic) {
+ if (!hasFunctionalConstraint) {
+ Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
+ + " has a deadline with no functional constraints."
+ + " The deadline won't improve job execution latency."
+ + " Consider removing the deadline.");
} else {
- if (maxExecutionDelayMillis - windowStart < 12 * HOUR_IN_MILLIS) {
- throw new IllegalArgumentException(
- getPriorityString(mPriority)
- + " cannot have a time window less than 12 hours."
- + " Delay=" + windowStart
- + ", deadline=" + maxExecutionDelayMillis);
+ final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
+ if (maxExecutionDelayMillis - windowStart < MIN_ALLOWED_TIME_WINDOW_MILLIS) {
+ if (enforceMinimumTimeWindows
+ && Flags.enforceMinimumTimeWindows()) {
+ throw new IllegalArgumentException("Jobs with a deadline and"
+ + " functional constraints cannot have a time window less than "
+ + MIN_ALLOWED_TIME_WINDOW_MILLIS + " ms."
+ + " Job '" + service.flattenToShortString() + "#" + jobId + "'"
+ + " has delay=" + windowStart
+ + ", deadline=" + maxExecutionDelayMillis);
+ } else {
+ Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
+ + " has a deadline with functional constraints and an extremely"
+ + " short time window of "
+ + (maxExecutionDelayMillis - windowStart) + " ms"
+ + " (delay=" + windowStart
+ + ", deadline=" + maxExecutionDelayMillis + ")."
+ + " The functional constraints are not likely to be satisfied when"
+ + " the job runs.");
+ }
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 2ea980d..a3a686f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -2053,6 +2053,11 @@
case CONSTRAINT_WITHIN_QUOTA:
return JobParameters.STOP_REASON_QUOTA;
+ // This can change from true to false, but should never change when a job is already
+ // running, so there's no reason to log a message or create a new stop reason.
+ case CONSTRAINT_FLEXIBLE:
+ return JobParameters.STOP_REASON_UNDEFINED;
+
// These should never be stop reasons since they can never go from true to false.
case CONSTRAINT_CONTENT_TRIGGER:
case CONSTRAINT_DEADLINE:
diff --git a/api/Android.bp b/api/Android.bp
index a148cbd..9d2147c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -361,7 +361,10 @@
previous_api: ":android.api.public.latest",
merge_annotations_dirs: ["metalava-manual"],
defaults_visibility: ["//frameworks/base/api"],
- visibility: ["//frameworks/base/api"],
+ visibility: [
+ "//frameworks/base/api",
+ "//frameworks/base/core/api",
+ ],
}
// We resolve dependencies on APIs in modules by depending on a prebuilt of the whole
diff --git a/api/api.go b/api/api.go
index fa2be21..c733f5b 100644
--- a/api/api.go
+++ b/api/api.go
@@ -130,7 +130,7 @@
Scope string
}
-func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) {
metalavaCmd := "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
@@ -140,7 +140,7 @@
if txt.Scope != "public" {
filename = txt.Scope + "-" + filename
}
- moduleName := ctx.ModuleName() + "-" + filename
+ moduleName := ctx.ModuleName() + stubsTypeSuffix + filename
props := genruleProps{}
props.Name = proptools.StringPtr(moduleName)
@@ -148,17 +148,19 @@
props.Out = []string{filename}
props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
- props.Dists = []android.Dist{
- {
- Targets: []string{"droidcore"},
- Dir: proptools.StringPtr("api"),
- Dest: proptools.StringPtr(filename),
- },
- {
- Targets: []string{"api_txt", "sdk"},
- Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
- Dest: proptools.StringPtr(txt.DistFilename),
- },
+ if doDist {
+ props.Dists = []android.Dist{
+ {
+ Targets: []string{"droidcore"},
+ Dir: proptools.StringPtr("api"),
+ Dest: proptools.StringPtr(filename),
+ },
+ {
+ Targets: []string{"api_txt", "sdk"},
+ Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+ Dest: proptools.StringPtr(txt.DistFilename),
+ },
+ }
}
props.Visibility = []string{"//visibility:public"}
ctx.CreateModule(genrule.GenRuleFactory, &props)
@@ -343,7 +345,7 @@
ctx.CreateModule(android.FileGroupFactory, &props)
}
-func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string, baseTxtModulePrefix, stubsTypeSuffix string, doDist bool) {
var textFiles []MergedTxtDefinition
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
@@ -352,7 +354,7 @@
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-" + f,
+ BaseTxt: ":" + baseTxtModulePrefix + f,
Modules: bootclasspath,
ModuleTag: "{.public" + tagSuffix[i],
Scope: "public",
@@ -360,7 +362,7 @@
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-system-" + f,
+ BaseTxt: ":" + baseTxtModulePrefix + "system-" + f,
Modules: bootclasspath,
ModuleTag: "{.system" + tagSuffix[i],
Scope: "system",
@@ -368,7 +370,7 @@
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-module-lib-" + f,
+ BaseTxt: ":" + baseTxtModulePrefix + "module-lib-" + f,
Modules: bootclasspath,
ModuleTag: "{.module-lib" + tagSuffix[i],
Scope: "module-lib",
@@ -376,14 +378,14 @@
textFiles = append(textFiles, MergedTxtDefinition{
TxtFilename: f,
DistFilename: distFilename[i],
- BaseTxt: ":non-updatable-system-server-" + f,
+ BaseTxt: ":" + baseTxtModulePrefix + "system-server-" + f,
Modules: system_server_classpath,
ModuleTag: "{.system-server" + tagSuffix[i],
Scope: "system-server",
})
}
for _, txt := range textFiles {
- createMergedTxt(ctx, txt)
+ createMergedTxt(ctx, txt, stubsTypeSuffix, doDist)
}
}
@@ -465,7 +467,8 @@
bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
sort.Strings(bootclasspath)
}
- createMergedTxts(ctx, bootclasspath, system_server_classpath)
+ createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
+ createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
createMergedPublicStubs(ctx, bootclasspath)
createMergedSystemStubs(ctx, bootclasspath)
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 8d8a82b..77594b7 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -96,3 +96,54 @@
name: "non-updatable-test-lint-baseline.txt",
srcs: ["test-lint-baseline.txt"],
}
+
+// Exportable stub artifacts
+filegroup {
+ name: "non-updatable-exportable-current.txt",
+ srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-removed.txt",
+ srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-system-current.txt",
+ srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-system-removed.txt",
+ srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-module-lib-current.txt",
+ srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-module-lib-removed.txt",
+ srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-test-current.txt",
+ srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-test-removed.txt",
+ srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-system-server-current.txt",
+ srcs: [":services-non-updatable-stubs{.exportable.api.txt}"],
+}
+
+filegroup {
+ name: "non-updatable-exportable-system-server-removed.txt",
+ srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"],
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 4eb7383..ddc7e02 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -765,7 +765,7 @@
field public static final int endY = 16844051; // 0x1010513
field @Deprecated public static final int endYear = 16843133; // 0x101017d
field public static final int enforceNavigationBarContrast = 16844293; // 0x1010605
- field public static final int enforceStatusBarContrast = 16844292; // 0x1010604
+ field @Deprecated public static final int enforceStatusBarContrast = 16844292; // 0x1010604
field public static final int enterFadeDuration = 16843532; // 0x101030c
field public static final int entries = 16842930; // 0x10100b2
field public static final int entryValues = 16843256; // 0x10101f8
@@ -1196,8 +1196,8 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625
- field public static final int navigationBarColor = 16843858; // 0x1010452
- field public static final int navigationBarDividerColor = 16844141; // 0x101056d
+ field @Deprecated public static final int navigationBarColor = 16843858; // 0x1010452
+ field @Deprecated public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -1568,7 +1568,7 @@
field public static final int state_single = 16842915; // 0x10100a3
field public static final int state_window_focused = 16842909; // 0x101009d
field public static final int staticWallpaperPreview = 16843569; // 0x1010331
- field public static final int statusBarColor = 16843857; // 0x1010451
+ field @Deprecated public static final int statusBarColor = 16843857; // 0x1010451
field public static final int stepSize = 16843078; // 0x1010146
field public static final int stopWithTask = 16843626; // 0x101036a
field public static final int streamType = 16843273; // 0x1010209
@@ -1890,6 +1890,7 @@
field public static final int windowNoDisplay = 16843294; // 0x101021e
field public static final int windowNoMoveAnimation = 16844421; // 0x1010685
field public static final int windowNoTitle = 16842838; // 0x1010056
+ field @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") public static final int windowOptOutEdgeToEdgeEnforcement;
field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf
field public static final int windowReenterTransition = 16843951; // 0x10104af
field public static final int windowReturnTransition = 16843950; // 0x10104ae
@@ -7894,6 +7895,7 @@
field public static final String AUTO_TIME_POLICY = "autoTime";
field public static final String BACKUP_SERVICE_POLICY = "backupService";
field public static final String CAMERA_DISABLED_POLICY = "cameraDisabled";
+ field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
field public static final String KEYGUARD_DISABLED_FEATURES_POLICY = "keyguardDisabledFeatures";
field public static final String LOCK_TASK_POLICY = "lockTask";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
@@ -7944,6 +7946,7 @@
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public boolean getCameraDisabled(@Nullable android.content.ComponentName);
method @Deprecated @Nullable public String getCertInstallerPackage(@NonNull android.content.ComponentName) throws java.lang.SecurityException;
+ method @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, conditional=true) public int getContentProtectionPolicy(@Nullable android.content.ComponentName);
method @Nullable public android.app.admin.PackagePolicy getCredentialManagerPolicy();
method @Deprecated @Nullable public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
method @Deprecated public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
@@ -8067,8 +8070,8 @@
method public boolean isUsbDataSignalingEnabled();
method public boolean isUsingUnifiedPassword(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<android.os.UserHandle> listForegroundAffiliatedUsers();
- method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow();
- method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow(int);
+ method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow();
+ method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow(int);
method public int logoutUser(@NonNull android.content.ComponentName);
method public void reboot(@NonNull android.content.ComponentName);
method public void removeActiveAdmin(@NonNull android.content.ComponentName);
@@ -8100,6 +8103,7 @@
method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException;
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, conditional=true) public void setCommonCriteriaModeEnabled(@Nullable android.content.ComponentName, boolean);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI, conditional=true) public void setConfiguredNetworksLockdownState(@Nullable android.content.ComponentName, boolean);
+ method @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, conditional=true) public void setContentProtectionPolicy(@Nullable android.content.ComponentName, int);
method public void setCredentialManagerPolicy(@Nullable android.app.admin.PackagePolicy);
method @Deprecated public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
method @Deprecated public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
@@ -8525,6 +8529,7 @@
field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+ field @FlaggedApi("android.app.admin.flags.backup_service_security_log_event_enabled") public static final int TAG_BACKUP_SERVICE_TOGGLED = 210044; // 0x3347c
field public static final int TAG_BLUETOOTH_CONNECTION = 210039; // 0x33477
field public static final int TAG_BLUETOOTH_DISCONNECTION = 210040; // 0x33478
field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
@@ -46299,6 +46304,7 @@
method @NonNull public android.telephony.euicc.EuiccManager createForCardId(int);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void deleteSubscription(int, android.app.PendingIntent);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
+ method @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "carrier privileges"}) public long getAvailableMemoryInBytes();
method @Nullable public String getEid();
method @Nullable public android.telephony.euicc.EuiccInfo getEuiccInfo();
method public boolean isEnabled();
@@ -46331,6 +46337,7 @@
field public static final int ERROR_SIM_MISSING = 10008; // 0x2718
field public static final int ERROR_TIME_OUT = 10005; // 0x2715
field public static final int ERROR_UNSUPPORTED_VERSION = 10007; // 0x2717
+ field @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") public static final long EUICC_MEMORY_FIELD_UNAVAILABLE = -1L; // 0xffffffffffffffffL
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
@@ -53619,8 +53626,8 @@
method @NonNull public abstract android.view.LayoutInflater getLayoutInflater();
method protected final int getLocalFeatures();
method public android.media.session.MediaController getMediaController();
- method @ColorInt public abstract int getNavigationBarColor();
- method @ColorInt public int getNavigationBarDividerColor();
+ method @Deprecated @ColorInt public abstract int getNavigationBarColor();
+ method @Deprecated @ColorInt public int getNavigationBarDividerColor();
method @NonNull public android.window.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method public android.transition.Transition getReenterTransition();
method public android.transition.Transition getReturnTransition();
@@ -53630,7 +53637,7 @@
method public android.transition.Transition getSharedElementReenterTransition();
method public android.transition.Transition getSharedElementReturnTransition();
method public boolean getSharedElementsUseOverlay();
- method @ColorInt public abstract int getStatusBarColor();
+ method @Deprecated @ColorInt public abstract int getStatusBarColor();
method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
method public long getTransitionBackgroundFadeDuration();
method public android.transition.TransitionManager getTransitionManager();
@@ -53646,7 +53653,7 @@
method public abstract boolean isFloating();
method public boolean isNavigationBarContrastEnforced();
method public abstract boolean isShortcutKey(int, android.view.KeyEvent);
- method public boolean isStatusBarContrastEnforced();
+ method @Deprecated public boolean isStatusBarContrastEnforced();
method public boolean isWideColorGamut();
method public final void makeActive();
method protected abstract void onActive();
@@ -53678,7 +53685,7 @@
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void setDecorCaptionShade(int);
- method public void setDecorFitsSystemWindows(boolean);
+ method @Deprecated public void setDecorFitsSystemWindows(boolean);
method protected void setDefaultWindowFormat(int);
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setDimAmount(float);
@@ -53700,9 +53707,9 @@
method public void setLocalFocus(boolean, boolean);
method public void setLogo(@DrawableRes int);
method public void setMediaController(android.media.session.MediaController);
- method public abstract void setNavigationBarColor(@ColorInt int);
+ method @Deprecated public abstract void setNavigationBarColor(@ColorInt int);
method public void setNavigationBarContrastEnforced(boolean);
- method public void setNavigationBarDividerColor(@ColorInt int);
+ method @Deprecated public void setNavigationBarDividerColor(@ColorInt int);
method public void setPreferMinimalPostProcessing(boolean);
method public void setReenterTransition(android.transition.Transition);
method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
@@ -53714,8 +53721,8 @@
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setSharedElementsUseOverlay(boolean);
method public void setSoftInputMode(int);
- method public abstract void setStatusBarColor(@ColorInt int);
- method public void setStatusBarContrastEnforced(boolean);
+ method @Deprecated public abstract void setStatusBarColor(@ColorInt int);
+ method @Deprecated public void setStatusBarContrastEnforced(boolean);
method public void setSustainedPerformanceMode(boolean);
method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
method public abstract void setTitle(CharSequence);
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 162f54c..e901f00 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -389,6 +389,12 @@
Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+KotlinOperator: android.graphics.Matrix44#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: android.graphics.Matrix44#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
@@ -477,6 +483,8 @@
Method 'clearResetPasswordToken' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#generateKeyPair(android.content.ComponentName, String, android.security.keystore.KeyGenParameterSpec, int):
Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getContentProtectionPolicy(android.content.ComponentName):
+ Method 'getContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
@@ -513,6 +521,8 @@
Method 'removeCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setContentProtectionPolicy(android.content.ComponentName, int):
+ Method 'setContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
Method 'setLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, String[]):
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index a2179bc..42c4efc 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -611,6 +611,8 @@
Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getContentProtectionPolicy(android.content.ComponentName):
+ Method 'getContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
@@ -657,6 +659,8 @@
Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setContentProtectionPolicy(android.content.ComponentName, int):
+ Method 'setContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
Method 'setDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 515d017..03b8141 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11,6 +11,7 @@
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
+ field @FlaggedApi("android.multiuser.flags.enable_permission_to_access_hidden_profiles") public static final String ACCESS_HIDDEN_PROFILES_FULL = "android.permission.ACCESS_HIDDEN_PROFILES_FULL";
field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String ACCESS_LAST_KNOWN_CELL_ID = "android.permission.ACCESS_LAST_KNOWN_CELL_ID";
field public static final String ACCESS_LOCUS_ID_USAGE_STATS = "android.permission.ACCESS_LOCUS_ID_USAGE_STATS";
@@ -3153,7 +3154,9 @@
public class WearableSensingManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
+ field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
field public static final int STATUS_SUCCESS = 1; // 0x1
field public static final int STATUS_UNKNOWN = 0; // 0x0
@@ -4249,7 +4252,6 @@
public final class UserProperties implements android.os.Parcelable {
method public int describeContents();
method public int getCrossProfileContentSharingStrategy();
- method @FlaggedApi("android.multiuser.support_hiding_profiles") @NonNull public int getProfileApiVisibility();
method public int getShowInQuietMode();
method public int getShowInSharingSurfaces();
method public boolean isCredentialShareableWithParent();
@@ -4259,9 +4261,6 @@
field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
field public static final int CROSS_PROFILE_CONTENT_SHARING_UNKNOWN = -1; // 0xffffffff
- field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_HIDDEN = 1; // 0x1
- field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1; // 0xffffffff
- field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_VISIBLE = 0; // 0x0
field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
@@ -4415,8 +4414,9 @@
}
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable {
+ ctor public CancelSelectionRequest(@NonNull android.credentials.selection.RequestToken, boolean, @NonNull String);
method public int describeContents();
- method @NonNull public String getAppPackageName();
+ method @NonNull public String getPackageName();
method @NonNull public android.credentials.selection.RequestToken getRequestToken();
method public boolean shouldShowCancellationExplanation();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -4498,10 +4498,10 @@
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public String getAppPackageName();
method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest();
method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds();
method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest();
+ method @NonNull public String getPackageName();
method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds();
method @NonNull public android.credentials.selection.RequestToken getRequestToken();
method @NonNull public String getType();
@@ -6954,6 +6954,8 @@
@FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
+ method public static long getDefaultFadeInDurationMillis();
+ method public static long getDefaultFadeOutDurationMillis();
method public long getFadeInDelayForOffenders();
method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
method public long getFadeInDurationForUsage(int);
@@ -6979,7 +6981,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.FadeManagerConfiguration> CREATOR;
field public static final long DURATION_NOT_SET = 0L; // 0x0L
field public static final int FADE_STATE_DISABLED = 0; // 0x0
- field public static final int FADE_STATE_ENABLED_AUTO = 2; // 0x2
field public static final int FADE_STATE_ENABLED_DEFAULT = 1; // 0x1
field public static final String TAG = "FadeManagerConfiguration";
field public static final int VOLUME_SHAPER_SYSTEM_FADE_ID = 2; // 0x2
@@ -6994,10 +6995,10 @@
method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableContentType(int);
method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableUid(int);
method @NonNull public android.media.FadeManagerConfiguration build();
- method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsage(int);
- method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
- method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentType(int);
- method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUid(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsages();
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes();
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentTypes();
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUids();
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
@@ -12404,6 +12405,7 @@
method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
method @Deprecated public abstract int onEraseSubscriptions(int);
method public int onEraseSubscriptions(int, int);
+ method @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") public long onGetAvailableMemoryInBytes(int);
method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
method @NonNull public android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean);
@@ -13433,6 +13435,7 @@
method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
method public abstract void onStopDetection(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService";
@@ -13608,6 +13611,13 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
+ public final class DisconnectCause implements android.os.Parcelable {
+ ctor @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public DisconnectCause(int, @NonNull CharSequence, @NonNull CharSequence, @NonNull String, int, int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable public android.telephony.ims.ImsReasonInfo getImsReasonInfo();
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyDisconnectCause();
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyPreciseDisconnectCause();
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -14180,12 +14190,12 @@
method @NonNull public android.telephony.DataThrottlingRequest.Builder setDataThrottlingAction(int);
}
- @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public class DomainSelectionService extends android.app.Service {
+ @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public abstract class DomainSelectionService extends android.app.Service {
ctor public DomainSelectionService();
method public void onBarringInfoUpdated(int, int, @NonNull android.telephony.BarringInfo);
- method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
method @NonNull public java.util.concurrent.Executor onCreateExecutor();
- method public void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback);
+ method public abstract void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback);
method public void onServiceStateUpdated(int, int, @NonNull android.telephony.ServiceState);
field public static final int SCAN_TYPE_FULL_SERVICE = 2; // 0x2
field public static final int SCAN_TYPE_LIMITED_SERVICE = 1; // 0x1
@@ -14199,7 +14209,7 @@
method @Nullable public android.net.Uri getAddress();
method @Nullable public String getCallId();
method public int getCsDisconnectCause();
- method @Nullable public android.telephony.EmergencyRegResult getEmergencyRegResult();
+ method @Nullable public android.telephony.EmergencyRegistrationResult getEmergencyRegistrationResult();
method @Nullable public android.telephony.ims.ImsReasonInfo getPsDisconnectCause();
method public int getSelectorType();
method public int getSlotIndex();
@@ -14215,13 +14225,13 @@
@FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final class DomainSelectionService.SelectionAttributes.Builder {
ctor public DomainSelectionService.SelectionAttributes.Builder(int, int, int);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes build();
- method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@NonNull android.net.Uri);
- method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@NonNull String);
+ method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@Nullable android.net.Uri);
+ method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@Nullable String);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCsDisconnectCause(int);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergency(boolean);
- method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegResult(@NonNull android.telephony.EmergencyRegResult);
+ method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegistrationResult(@Nullable android.telephony.EmergencyRegistrationResult);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setExitedFromAirplaneMode(boolean);
- method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@NonNull android.telephony.ims.ImsReasonInfo);
+ method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@Nullable android.telephony.ims.ImsReasonInfo);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setTestEmergencyNumber(boolean);
method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setVideoCall(boolean);
}
@@ -14231,7 +14241,7 @@
method public void reselectDomain(@NonNull android.telephony.DomainSelectionService.SelectionAttributes);
}
- @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegResult implements android.os.Parcelable {
+ @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegistrationResult implements android.os.Parcelable {
method public int describeContents();
method public int getAccessNetwork();
method @NonNull public String getCountryIso();
@@ -14244,7 +14254,7 @@
method public boolean isEmcBearerSupported();
method public boolean isVopsSupported();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegResult> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegistrationResult> CREATOR;
}
public final class ImsiEncryptionInfo implements android.os.Parcelable {
@@ -15316,7 +15326,7 @@
@FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public interface WwanSelectorCallback {
method public void onDomainSelected(int, boolean);
- method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegResult>);
+ method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegistrationResult>);
}
}
@@ -17384,6 +17394,19 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public class EnableRequestAttributes {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isDemoMode();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEmergencyMode();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEnabled();
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final class EnableRequestAttributes.Builder {
+ ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public EnableRequestAttributes.Builder(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes build();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes.Builder setDemoMode(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes.Builder setEmergencyMode(boolean);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class NtnSignalStrength implements android.os.Parcelable {
ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public NtnSignalStrength(@Nullable android.telephony.satellite.NtnSignalStrength);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
@@ -17449,10 +17472,11 @@
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @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 requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestEnabled(boolean, boolean, @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 requestEnabled(@NonNull android.telephony.satellite.EnableRequestAttributes, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
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>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsEmergencyModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -17514,6 +17538,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; // 0x18
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_ERROR = 5; // 0x5
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17; // 0x11
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19; // 0x13
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 6c83fd0..8a485d2 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -525,6 +525,10 @@
Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
+KotlinOperator: android.hardware.camera2.extension.CharacteristicsMap#get(String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>) parameter #1:
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>) parameter #2:
@@ -685,6 +689,8 @@
Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getContentProtectionPolicy(android.content.ComponentName):
+ Method 'getContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
@@ -731,6 +737,8 @@
Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setContentProtectionPolicy(android.content.ComponentName, int):
+ Method 'setContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
Method 'setDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
@@ -2305,14 +2313,14 @@
New API must be flagged with @FlaggedApi: constructor android.telephony.satellite.SatelliteManager.SatelliteException(int)
UnflaggedApi: android.telephony.satellite.SatelliteManager.SatelliteException#getErrorCode():
New API must be flagged with @FlaggedApi: method android.telephony.satellite.SatelliteManager.SatelliteException.getErrorCode()
-UnflaggedApi: android.telephony.satellite.SatelliteProvisionStateCallback:
- 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.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.SatelliteProvisionStateCallback:
+ 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.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/api/test-current.txt b/core/api/test-current.txt
index 4a048bd..b17f853 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -624,6 +624,7 @@
field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
field public static final int OPERATION_SET_CAMERA_DISABLED = 31; // 0x1f
+ field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41; // 0x29
field public static final int OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY = 32; // 0x20
field public static final int OPERATION_SET_GLOBAL_PRIVATE_DNS = 33; // 0x21
field public static final int OPERATION_SET_KEEP_UNINSTALLED_PACKAGES = 17; // 0x11
@@ -1282,10 +1283,6 @@
field public static final int RESULT_CODE_DIALOG_USER_CANCELED = 0; // 0x0
}
- @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable {
- ctor @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public CancelSelectionRequest(@NonNull android.os.IBinder, boolean, @NonNull String);
- }
-
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable {
ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @Nullable android.credentials.selection.Entry);
method @Nullable public android.credentials.selection.Entry getRemoteEntry();
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 5e904ef9..b938f0f 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -673,6 +673,8 @@
Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getContentProtectionPolicy(android.content.ComponentName):
+ Method 'getContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
@@ -721,6 +723,8 @@
Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setContentProtectionPolicy(android.content.ComponentName, int):
+ Method 'setContentProtectionPolicy' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceOwner(android.content.ComponentName, int):
Method 'setDeviceOwner' documentation mentions permissions already declared by @RequiresPermission
RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0a34d36..aa9de81 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,6 +23,7 @@
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
+import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import static java.util.Objects.requireNonNull;
@@ -3540,15 +3541,12 @@
* Sets the token used for background operations for the pending intents associated with this
* notification.
*
- * This token is automatically set during deserialization for you, you usually won't need to
- * call this unless you want to change the existing token, if any.
- *
* @hide
*/
- public void clearAllowlistToken() {
- mAllowlistToken = null;
+ public void overrideAllowlistToken(IBinder token) {
+ mAllowlistToken = token;
if (publicVersion != null) {
- publicVersion.clearAllowlistToken();
+ publicVersion.overrideAllowlistToken(token);
}
}
@@ -5957,7 +5955,7 @@
// there is enough space to do so (and fall back to the left edge if not).
big.setInt(R.id.actions, "setCollapsibleIndentDimen",
R.dimen.call_notification_collapsible_indent);
- if (CallStyle.USE_NEW_ACTION_LAYOUT) {
+ if (evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "setting evenly divided mode on action list");
}
@@ -6439,7 +6437,7 @@
title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
}
final CharSequence label = ensureColorSpanContrast(title, p);
- if (p.mCallStyleActions && CallStyle.USE_NEW_ACTION_LAYOUT) {
+ if (p.mCallStyleActions && evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "new action layout enabled, gluing instead of setting text");
}
@@ -6463,7 +6461,7 @@
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(buttonFillColor));
if (p.mCallStyleActions) {
- if (CallStyle.USE_NEW_ACTION_LAYOUT) {
+ if (evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "new action layout enabled, gluing instead of setting icon");
}
@@ -9600,11 +9598,6 @@
/**
* @hide
*/
- public static final boolean USE_NEW_ACTION_LAYOUT = false;
-
- /**
- * @hide
- */
public static final boolean DEBUG_NEW_ACTION_LAYOUT = true;
/**
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index b0bec78..d7aafa0 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -22,7 +22,6 @@
import android.app.admin.flags.Flags;
import android.os.UserManager;
-
import java.util.Objects;
/**
@@ -163,6 +162,12 @@
public static final String CROSS_PROFILE_WIDGET_PROVIDER_POLICY = "crossProfileWidgetProvider";
/**
+ * String identifier for {@link DevicePolicyManager#setContentProtectionPolicy}.
+ */
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ public static final String CONTENT_PROTECTION_POLICY = "contentProtection";
+
+ /**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
@FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 86d0125..c8762c6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,12 +18,14 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.LOCK_DEVICE;
import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS;
@@ -53,7 +55,6 @@
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
-import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -3825,6 +3826,10 @@
/** @hide */
@TestApi
public static final int OPERATION_UNINSTALL_CA_CERT = 40;
+ /** @hide */
+ @TestApi
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ public static final int OPERATION_SET_CONTENT_PROTECTION_POLICY = 41;
private static final String PREFIX_OPERATION = "OPERATION_";
@@ -3869,7 +3874,8 @@
OPERATION_SET_PERMISSION_GRANT_STATE,
OPERATION_SET_PERMISSION_POLICY,
OPERATION_SET_RESTRICTIONS_PROVIDER,
- OPERATION_UNINSTALL_CA_CERT
+ OPERATION_UNINSTALL_CA_CERT,
+ OPERATION_SET_CONTENT_PROTECTION_POLICY
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DevicePolicyOperation {
@@ -4095,15 +4101,15 @@
}
/** Indicates that content protection is not controlled by policy, allowing user to choose. */
- @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0;
- /** Indicates that content protection is controlled and disabled by a policy. */
- @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ /** Indicates that content protection is controlled and disabled by a policy (default). */
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int CONTENT_PROTECTION_DISABLED = 1;
/** Indicates that content protection is controlled and enabled by a policy. */
- @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int CONTENT_PROTECTION_ENABLED = 2;
/** @hide */
@@ -4118,6 +4124,86 @@
public @interface ContentProtectionPolicy {}
/**
+ * Sets the content protection policy which controls scanning for deceptive apps.
+ * <p>
+ * This function can only be called by the device owner, a profile owner of an affiliated user
+ * or profile, or the profile owner when no device owner is set or holders of the permission
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CONTENT_PROTECTION}. See
+ * {@link #isAffiliatedUser}.
+ * Any policy set via this method will be cleared if the user becomes unaffiliated.
+ * <p>
+ * After the content protection policy has been set,
+ * {@link PolicyUpdateReceiver#onPolicySetResult(Context, String, Bundle, TargetUser,
+ * PolicyUpdateResult)} will notify the admin on whether the policy was successfully set or not.
+ * This callback will contain:
+ * <ul>
+ * <li> The policy identifier {@link DevicePolicyIdentifiers#CONTENT_PROTECTION_POLICY}
+ * <li> The {@link TargetUser} that this policy relates to
+ * <li> The {@link PolicyUpdateResult}, which will be
+ * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+ * reason the policy failed to be set
+ * (e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+ * </ul>
+ * If there has been a change to the policy,
+ * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+ * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+ * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+ * will contain the reason why the policy changed.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
+ * caller is not a device admin.
+ * @param policy The content protection policy to set. One of {@link
+ * #CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY},
+ * {@link #CONTENT_PROTECTION_DISABLED} or {@link #CONTENT_PROTECTION_ENABLED}.
+ * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
+ * affiliated user or profile, or the profile owner when no device owner is set or holder of the
+ * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CONTENT_PROTECTION}.
+ * @see #isAffiliatedUser
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, conditional = true)
+ @SupportsCoexistence
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ public void setContentProtectionPolicy(
+ @Nullable ComponentName admin, @ContentProtectionPolicy int policy) {
+ throwIfParentInstance("setContentProtectionPolicy");
+ if (mService != null) {
+ try {
+ mService.setContentProtectionPolicy(admin, mContext.getPackageName(), policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current content protection policy.
+ * <p>
+ * The returned policy will be the current resolved policy rather than the policy set by the
+ * calling admin.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
+ * caller is not a device admin.
+ * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
+ * affiliated user or profile, or the profile owner when no device owner is set or holder of the
+ * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CONTENT_PROTECTION}.
+ * @see #isAffiliatedUser
+ * @see #setContentProtectionPolicy
+ */
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, conditional = true)
+ @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+ public @ContentProtectionPolicy int getContentProtectionPolicy(@Nullable ComponentName admin) {
+ throwIfParentInstance("getContentProtectionPolicy");
+ if (mService != null) {
+ try {
+ return mService.getContentProtectionPolicy(admin, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return CONTENT_PROTECTION_DISABLED;
+ }
+
+ /**
* This object is a single place to tack on invalidation and disable calls. All
* binder caches in this class derive from this Config, so all can be invalidated or
* disabled through this Config.
@@ -6330,10 +6416,10 @@
* (PIN, pattern, or password). This API is intended for use only by device admins.
* <p>
* From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
- * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
- * true, then the method will return without completing any action. Before version
- * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
- * regardless of the caller's permissions.
+ * the LOCK_DEVICE permission or the device must have the
+ * device admin feature; if neither is true, then the method will return without completing
+ * any action. Before version {@link android.os.Build.VERSION_CODES#R},
+ * the device needed the device admin feature, regardless of the caller's permissions.
* <p>
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
@@ -6353,7 +6439,8 @@
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
*/
- @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true)
+ @SuppressLint("RequiresPermission")
+ @RequiresPermission(value = LOCK_DEVICE, conditional = true)
public void lockNow() {
lockNow(0);
}
@@ -6364,14 +6451,13 @@
* <p>
* This method secures the device in response to an urgent situation, such as a lost or stolen
* device. After this method is called, the device must be unlocked using strong authentication
- * (PIN, pattern, or password). This API is for use only by device admins and holders of the
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission.
+ * (PIN, pattern, or password). This API is intended for use only by device admins.
* <p>
* From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
- * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
- * true, then the method will return without completing any action. Before version
- * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
- * regardless of the caller's permissions.
+ * the LOCK_DEVICE permission or the device must have the
+ * device admin feature; if neither is true, then the method will return without completing any
+ * action. Before version {@link android.os.Build.VERSION_CODES#R}, the device needed the device
+ * admin feature, regardless of the caller's permissions.
* <p>
* A calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
@@ -6400,7 +6486,7 @@
* @param flags May be 0 or {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}.
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} and the does not hold
- * the {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission, or
+ * the {@link android.Manifest.permission#LOCK_DEVICE} permission, or
* the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is passed by an
* application that is not a profile owner of a managed profile.
* @throws IllegalArgumentException if the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is
@@ -6409,7 +6495,7 @@
* flag is passed when {@link #getStorageEncryptionStatus} does not return
* {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}.
*/
- @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true)
+ @RequiresPermission(value = LOCK_DEVICE, conditional = true)
public void lockNow(@LockNowFlag int flags) {
if (mService != null) {
try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 575fa4c..efcf563 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -610,4 +610,7 @@
String getFinancedDeviceKioskRoleHolder(String callerPackageName);
void calculateHasIncompatibleAccounts();
+
+ void setContentProtectionPolicy(in ComponentName who, String callerPackageName, int policy);
+ int getContentProtectionPolicy(in ComponentName who, String callerPackageName);
}
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index ca2e97e..ed1b8ca 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -17,12 +17,14 @@
package android.app.admin;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.os.Build;
@@ -99,6 +101,7 @@
TAG_PACKAGE_INSTALLED,
TAG_PACKAGE_UPDATED,
TAG_PACKAGE_UNINSTALLED,
+ TAG_BACKUP_SERVICE_TOGGLED,
})
public @interface SecurityLogTag {}
@@ -599,6 +602,18 @@
public static final int TAG_PACKAGE_UNINSTALLED = SecurityLogTags.SECURITY_PACKAGE_UNINSTALLED;
/**
+ * Indicates that an admin has enabled or disabled backup service. The log entry contains the
+ * following information about the event encapsulated in an {@link Object} array, accessible
+ * via {@link SecurityEvent#getData()}:
+ * <li> [0] admin package name ({@code String})
+ * <li> [1] admin user ID ({@code Integer})
+ * <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
+ * @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
+ */
+ @FlaggedApi(Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
+ public static final int TAG_BACKUP_SERVICE_TOGGLED =
+ SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index e4af8dd..7b3aa7b 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -47,4 +47,5 @@
210040 security_bluetooth_disconnection (addr|3),(reason|3)
210041 security_package_installed (package_name|3),(version_code|1),(user_id|1)
210042 security_package_updated (package_name|3),(version_code|1),(user_id|1)
-210043 security_package_uninstalled (package_name|3),(version_code|1),(user_id|1)
\ No newline at end of file
+210043 security_package_uninstalled (package_name|3),(version_code|1),(user_id|1)
+210044 security_backup_service_toggled (package|3),(admin_user|1),(enabled|1)
\ No newline at end of file
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index b3ecd92..561eb00 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -62,3 +62,10 @@
description: "Exempt the default sms app of the context user for suspension when calling setPersonalAppsSuspended"
bug: "309183330"
}
+
+flag {
+ name: "backup_service_security_log_event_enabled"
+ namespace: "enterprise"
+ description: "Emit a security log event when DPM.setBackupServiceEnabled is called"
+ bug: "304999634"
+}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index c40b23e..274d02a 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -56,4 +56,14 @@
namespace: "systemui"
description: "This flag enables the API to allow setting VibrationEffect for NotificationChannels"
bug: "241732519"
+}
+
+flag {
+ name: "evenly_divided_call_style_action_layout"
+ namespace: "systemui"
+ description: "Evenly divides horizontal space for action buttons in CallStyle notifications."
+ bug: "268733030"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
index ff37bd8..9d55ce28 100644
--- a/core/java/android/app/wearable/IWearableSensingManager.aidl
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -28,6 +28,8 @@
*/
interface IWearableSensingManager {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+ void provideWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index eca0039..401d0b7 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -26,6 +26,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ambientcontext.AmbientContextEvent;
+import android.companion.CompanionDeviceManager;
import android.content.Context;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,8 @@
import android.service.wearable.WearableSensingService;
import android.system.OsConstants;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
@@ -107,6 +110,14 @@
@FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE)
public static final int STATUS_UNSUPPORTED_OPERATION = 6;
+ /**
+ * The value of the status code that indicates an error occurred in the encrypted channel backed
+ * by the provided connection. See {@link #provideWearableConnection(ParcelFileDescriptor,
+ * Executor, Consumer)}.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
+ public static final int STATUS_CHANNEL_ERROR = 7;
+
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_UNKNOWN,
@@ -115,7 +126,8 @@
STATUS_SERVICE_UNAVAILABLE,
STATUS_WEARABLE_UNAVAILABLE,
STATUS_ACCESS_DENIED,
- STATUS_UNSUPPORTED_OPERATION
+ STATUS_UNSUPPORTED_OPERATION,
+ STATUS_CHANNEL_ERROR
})
@Retention(RetentionPolicy.SOURCE)
public @interface StatusCode {}
@@ -132,6 +144,60 @@
}
/**
+ * Provides a remote wearable device connection to the WearableSensingService and sends the
+ * resulting status to the {@code statusConsumer} after the call.
+ *
+ * <p>This is used by applications that will also provide an implementation of the isolated
+ * WearableSensingService.
+ *
+ * <p>The provided {@code wearableConnection} is expected to be a connection to a remotely
+ * connected wearable device. This {@code wearableConnection} will be attached to
+ * CompanionDeviceManager via {@link CompanionDeviceManager#attachSystemDataTransport(int,
+ * InputStream, OutputStream)}, which will create an encrypted channel using {@code
+ * wearableConnection} as the raw underlying connection. The wearable device is expected to
+ * attach its side of the raw connection to its CompanionDeviceManager via the same method so
+ * that the two CompanionDeviceManagers on the two devices can perform attestation and set up
+ * the encrypted channel. Attestation requirements are listed in
+ * com.android.server.security.AttestationVerificationPeerDeviceVerifier
+ *
+ * <p>A proxy to the encrypted channel will be provided to the WearableSensingService, which is
+ * referred to as the secureWearableConnection in WearableSensingService. Any data written to
+ * secureWearableConnection will be encrypted by CompanionDeviceManager and sent over the raw
+ * {@code wearableConnection} to the remote wearable device, which is expected to use its
+ * CompanionDeviceManager to decrypt the data. Encrypted data arriving at the raw {@code
+ * wearableConnection} will be decrypted by CompanionDeviceManager and be readable as plain text
+ * from secureWearableConnection. The raw {@code wearableConnection} provided to this method
+ * will not be directly available to the WearableSensingService.
+ *
+ * <p>If an error occurred in the encrypted channel (such as the underlying stream closed), the
+ * system will send a status code of {@link STATUS_CHANNEL_ERROR} to the {@code statusConsumer}
+ * and kill the WearableSensingService process.
+ *
+ * <p>Before providing the secureWearableConnection, the system will restart the
+ * WearableSensingService process. Other method calls into WearableSensingService may be dropped
+ * during the restart. The caller is responsible for ensuring other method calls are queued
+ * until a success status is returned from the {@code statusConsumer}.
+ *
+ * @param wearableConnection The connection to provide
+ * @param executor Executor on which to run the consumer callback
+ * @param statusConsumer A consumer that handles the status codes for providing the connection
+ * and errors in the encrypted channel.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+ @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
+ public void provideWearableConnection(
+ @NonNull ParcelFileDescriptor wearableConnection,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+ try {
+ RemoteCallback callback = createStatusCallback(executor, statusConsumer);
+ mService.provideWearableConnection(wearableConnection, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Provides a data stream to the WearableSensingService that's backed by the
* parcelFileDescriptor, and sends the result to the {@link Consumer} right after the call.
* This is used by applications that will also provide an implementation of
@@ -149,15 +215,7 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull @StatusCode Consumer<Integer> statusConsumer) {
try {
- RemoteCallback callback = new RemoteCallback(result -> {
- int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> statusConsumer.accept(status));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- });
+ RemoteCallback callback = createStatusCallback(executor, statusConsumer);
mService.provideDataStream(parcelFileDescriptor, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -191,19 +249,24 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull @StatusCode Consumer<Integer> statusConsumer) {
try {
- RemoteCallback callback = new RemoteCallback(result -> {
- int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> statusConsumer.accept(status));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- });
+ RemoteCallback callback = createStatusCallback(executor, statusConsumer);
mService.provideData(data, sharedMemory, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ private static RemoteCallback createStatusCallback(
+ Executor executor, Consumer<Integer> statusConsumer) {
+ return new RemoteCallback(
+ result -> {
+ int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> statusConsumer.accept(status));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ }
}
diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
index c8e7cae..2e7f19e 100644
--- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl
+++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
@@ -17,10 +17,15 @@
package android.content.pm;
import android.content.pm.ParceledListSlice;
+import android.os.IRemoteCallback;
/**
* {@hide}
*/
interface IBackgroundInstallControlService {
ParceledListSlice getBackgroundInstalledPackages(long flags, int userId);
+
+ void registerBackgroundInstallCallback(IRemoteCallback callback);
+
+ void unregisterBackgroundInstallCallback(IRemoteCallback callback);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 08f1853..bff90f1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -846,4 +846,6 @@
@EnforcePermission("GET_APP_METADATA")
int getAppMetadataSource(String packageName, int userId);
+
+ ComponentName getDomainVerificationAgent();
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index f54b2ac..d347a0e 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -16,9 +16,6 @@
package android.content.pm;
-import static android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES;
-
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -476,22 +473,26 @@
)
public @interface ProfileApiVisibility {
}
- /*
- * The api visibility value for this profile user is undefined or unknown.
+
+ /**
+ * The api visibility value for this profile user is undefined or unknown.
+ *
+ * @hide
*/
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1;
/**
* Indicates that information about this profile user should be shown in API surfaces.
+ *
+ * @hide
*/
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public static final int PROFILE_API_VISIBILITY_VISIBLE = 0;
/**
* Indicates that information about this profile should be not be visible in API surfaces.
+ *
+ * @hide
*/
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public static final int PROFILE_API_VISIBILITY_HIDDEN = 1;
@@ -555,9 +556,7 @@
setShowInQuietMode(orig.getShowInQuietMode());
setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
- if (android.multiuser.Flags.supportHidingProfiles()) {
- setProfileApiVisibility(orig.getProfileApiVisibility());
- }
+ setProfileApiVisibility(orig.getProfileApiVisibility());
}
/**
@@ -1002,9 +1001,10 @@
/**
* Returns the visibility of the profile user in API surfaces. Any information linked to the
* profile (userId, package names) should be hidden API surfaces if a user is marked as hidden.
+ *
+ * @hide
*/
@NonNull
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public @ProfileApiVisibility int getProfileApiVisibility() {
if (isPresent(INDEX_PROFILE_API_VISIBILITY)) return mProfileApiVisibility;
if (mDefaultProperties != null) return mDefaultProperties.mProfileApiVisibility;
@@ -1012,7 +1012,6 @@
}
/** @hide */
@NonNull
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public void setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility) {
this.mProfileApiVisibility = profileApiVisibility;
setPresent(INDEX_PROFILE_API_VISIBILITY);
@@ -1053,9 +1052,6 @@
@Override
public String toString() {
- String profileApiVisibility =
- android.multiuser.Flags.supportHidingProfiles() ? ", mProfileApiVisibility="
- + getProfileApiVisibility() : "";
// Please print in increasing order of PropertyIndex.
return "UserProperties{"
+ "mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent)
@@ -1079,7 +1075,7 @@
+ ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ ", mAlwaysVisible=" + getAlwaysVisible()
+ ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
- + ", mProfileApiVisibility=" + profileApiVisibility
+ + ", mProfileApiVisibility=" + getProfileApiVisibility()
+ ", mItemsRestrictedOnHomeScreen=" + areItemsRestrictedOnHomeScreen()
+ "}";
}
@@ -1114,9 +1110,7 @@
pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible());
pw.println(prefix + " mCrossProfileContentSharingStrategy="
+ getCrossProfileContentSharingStrategy());
- if (android.multiuser.Flags.supportHidingProfiles()) {
- pw.println(prefix + " mProfileApiVisibility=" + getProfileApiVisibility());
- }
+ pw.println(prefix + " mProfileApiVisibility=" + getProfileApiVisibility());
pw.println(prefix + " mItemsRestrictedOnHomeScreen=" + areItemsRestrictedOnHomeScreen());
}
@@ -1203,9 +1197,7 @@
setCrossProfileContentSharingStrategy(parser.getAttributeInt(i));
break;
case ATTR_PROFILE_API_VISIBILITY:
- if (android.multiuser.Flags.supportHidingProfiles()) {
- setProfileApiVisibility(parser.getAttributeInt(i));
- }
+ setProfileApiVisibility(parser.getAttributeInt(i));
break;
case ITEMS_RESTRICTED_ON_HOME_SCREEN:
setItemsRestrictedOnHomeScreen(parser.getAttributeBoolean(i));
@@ -1293,10 +1285,8 @@
mCrossProfileContentSharingStrategy);
}
if (isPresent(INDEX_PROFILE_API_VISIBILITY)) {
- if (android.multiuser.Flags.supportHidingProfiles()) {
- serializer.attributeInt(null, ATTR_PROFILE_API_VISIBILITY,
- mProfileApiVisibility);
- }
+ serializer.attributeInt(null, ATTR_PROFILE_API_VISIBILITY,
+ mProfileApiVisibility);
}
if (isPresent(INDEX_ITEMS_RESTRICTED_ON_HOME_SCREEN)) {
serializer.attributeBoolean(null, ITEMS_RESTRICTED_ON_HOME_SCREEN,
@@ -1566,7 +1556,6 @@
* @hide
*/
@NonNull
- @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
public Builder setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility){
mProfileApiVisibility = profileApiVisibility;
return this;
@@ -1650,9 +1639,7 @@
setDeleteAppWithParent(deleteAppWithParent);
setAlwaysVisible(alwaysVisible);
setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy);
- if (android.multiuser.Flags.supportHidingProfiles()) {
- setProfileApiVisibility(profileApiVisibility);
- }
+ setProfileApiVisibility(profileApiVisibility);
setItemsRestrictedOnHomeScreen(itemsRestrictedOnHomeScreen);
}
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 7ded747..4b890fa 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -109,18 +109,10 @@
}
flag {
- name: "allow_private_profile_apis"
+ name: "enable_private_space_features"
namespace: "profile_experiences"
- description: "Enable only the API changes to support private space"
- bug: "299069460"
-}
-
-flag {
- name: "support_hiding_profiles"
- namespace: "profile_experiences"
- description: "Allow the use of a hide_profile property to hide some profiles behind a permission"
- bug: "316362775"
- is_fixed_read_only: true
+ description: "Enable the support for private space and all its sub-features"
+ bug: "286418785"
}
flag {
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
index 8e28042..6c4fe37 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationState.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -33,7 +33,8 @@
STATE_DENIED,
STATE_LEGACY_FAILURE,
STATE_SYS_CONFIG,
- STATE_FIRST_VERIFIER_DEFINED
+ STATE_PRE_VERIFIED,
+ STATE_FIRST_VERIFIER_DEFINED,
})
@interface State {
}
@@ -92,6 +93,13 @@
int STATE_SYS_CONFIG = 7;
/**
+ * The application has temporarily been granted auto verification for a set of domains as
+ * specified by a trusted installer during the installation. This will treat the domain as
+ * verified, but it should be updated by the verification agent.
+ */
+ int STATE_PRE_VERIFIED = 8;
+
+ /**
* @see DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED
*/
int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
@@ -115,6 +123,8 @@
return "legacy_failure";
case DomainVerificationState.STATE_SYS_CONFIG:
return "system_configured";
+ case DomainVerificationState.STATE_PRE_VERIFIED:
+ return "pre_verified";
default:
return String.valueOf(state);
}
@@ -135,6 +145,7 @@
case STATE_DENIED:
case STATE_LEGACY_FAILURE:
case STATE_SYS_CONFIG:
+ case STATE_PRE_VERIFIED:
default:
return false;
}
@@ -151,6 +162,7 @@
case DomainVerificationState.STATE_MIGRATED:
case DomainVerificationState.STATE_RESTORED:
case DomainVerificationState.STATE_SYS_CONFIG:
+ case DomainVerificationState.STATE_PRE_VERIFIED:
return true;
case DomainVerificationState.STATE_NO_RESPONSE:
case DomainVerificationState.STATE_DENIED:
@@ -173,6 +185,7 @@
case DomainVerificationState.STATE_MIGRATED:
case DomainVerificationState.STATE_RESTORED:
case DomainVerificationState.STATE_LEGACY_FAILURE:
+ case DomainVerificationState.STATE_PRE_VERIFIED:
return true;
case DomainVerificationState.STATE_APPROVED:
case DomainVerificationState.STATE_DENIED:
@@ -194,6 +207,7 @@
case STATE_RESTORED:
case STATE_APPROVED:
case STATE_DENIED:
+ case STATE_PRE_VERIFIED:
return true;
case STATE_NO_RESPONSE:
case STATE_LEGACY_FAILURE:
diff --git a/core/java/android/credentials/Constants.java b/core/java/android/credentials/Constants.java
new file mode 100644
index 0000000..ea30c44
--- /dev/null
+++ b/core/java/android/credentials/Constants.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.credentials;
+
+/**
+ * Constants for credential manager service that doesn't fit into other structures
+ *
+ * @hide
+ */
+public class Constants {
+ /**
+ * The request is success and user selected an entry
+ */
+ public static final int SUCCESS_CREDMAN_SELECTOR = 0;
+ /**
+ * The error code for ui getting cancelled by user
+ */
+ public static final int FAILURE_CREDMAN_SELECTOR = -1;
+}
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 73361ad..6e53fd9 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -41,8 +41,6 @@
private final PendingIntent mPendingIntent;
- private final GetCredentialResponse mGetCredentialResponse;
-
/**
* @hide
*/
@@ -52,7 +50,6 @@
) {
mCandidateProviderDataList = null;
mPendingIntent = null;
- mGetCredentialResponse = getCredentialResponse;
}
/**
@@ -68,7 +65,6 @@
/*valueName=*/ "candidateProviderDataList");
mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
mPendingIntent = pendingIntent;
- mGetCredentialResponse = null;
}
/**
@@ -85,15 +81,6 @@
*
* @hide
*/
- public GetCredentialResponse getGetCredentialResponse() {
- return mGetCredentialResponse;
- }
-
- /**
- * Returns candidate provider data list.
- *
- * @hide
- */
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
@@ -106,14 +93,12 @@
AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
- mGetCredentialResponse = in.readTypedObject(GetCredentialResponse.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(mCandidateProviderDataList);
dest.writeTypedObject(mPendingIntent, flags);
- dest.writeTypedObject(mGetCredentialResponse, flags);
}
@Override
diff --git a/core/java/android/credentials/selection/CancelSelectionRequest.java b/core/java/android/credentials/selection/CancelSelectionRequest.java
index 2662d76..55acfdb 100644
--- a/core/java/android/credentials/selection/CancelSelectionRequest.java
+++ b/core/java/android/credentials/selection/CancelSelectionRequest.java
@@ -21,7 +21,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,7 +58,7 @@
private final boolean mShouldShowCancellationExplanation;
@NonNull
- private final String mAppPackageName;
+ private final String mPackageName;
/**
* Returns the request token matching the user request that should be cancelled.
@@ -85,8 +84,8 @@
* metadata (e.g. "Cancelled by `App Name`").
*/
@NonNull
- public String getAppPackageName() {
- return mAppPackageName;
+ public String getPackageName() {
+ return mPackageName;
}
/**
@@ -98,33 +97,36 @@
return mShouldShowCancellationExplanation;
}
+
/**
* Constructs a {@link CancelSelectionRequest}.
*
- * @hide
+ * @param requestToken request token matching the app request that should be cancelled
+ * @param shouldShowCancellationExplanation whether the UI should display some informational
+ * cancellation message before closing
+ * @param packageName package that is invoking this request
+ *
*/
- @TestApi
- @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED)
- public CancelSelectionRequest(@NonNull IBinder token, boolean shouldShowCancellationExplanation,
- @NonNull String appPackageName) {
- mToken = token;
+ public CancelSelectionRequest(@NonNull RequestToken requestToken,
+ boolean shouldShowCancellationExplanation, @NonNull String packageName) {
+ mToken = requestToken.getToken();
mShouldShowCancellationExplanation = shouldShowCancellationExplanation;
- mAppPackageName = appPackageName;
+ mPackageName = packageName;
}
private CancelSelectionRequest(@NonNull Parcel in) {
mToken = in.readStrongBinder();
AnnotationValidations.validate(NonNull.class, null, mToken);
mShouldShowCancellationExplanation = in.readBoolean();
- mAppPackageName = in.readString8();
- AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
+ mPackageName = in.readString8();
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mToken);
dest.writeBoolean(mShouldShowCancellationExplanation);
- dest.writeString8(mAppPackageName);
+ dest.writeString8(mPackageName);
}
@Override
diff --git a/core/java/android/credentials/selection/Constants.java b/core/java/android/credentials/selection/Constants.java
index 7e6c781..f7fec23 100644
--- a/core/java/android/credentials/selection/Constants.java
+++ b/core/java/android/credentials/selection/Constants.java
@@ -36,5 +36,11 @@
public static final String EXTRA_REQ_FOR_ALL_OPTIONS =
"android.credentials.selection.extra.REQ_FOR_ALL_OPTIONS";
+ /**
+ * The intent extra key for the final result receiver object
+ */
+ public static final String EXTRA_FINAL_RESPONSE_RECEIVER =
+ "android.credentials.selection.extra.FINAL_RESPONSE_RECEIVER";
+
private Constants() {}
}
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index 1837976..ac2bae4 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -210,7 +210,8 @@
.config_credentialManagerDialogComponent));
intent.setComponent(componentName);
intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST,
- new CancelSelectionRequest(requestToken, shouldShowCancellationUi, appPackageName));
+ new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi,
+ appPackageName));
return intent;
}
diff --git a/core/java/android/credentials/selection/RequestInfo.java b/core/java/android/credentials/selection/RequestInfo.java
index 60bbae6..16d0802 100644
--- a/core/java/android/credentials/selection/RequestInfo.java
+++ b/core/java/android/credentials/selection/RequestInfo.java
@@ -106,7 +106,7 @@
private final String mType;
@NonNull
- private final String mAppPackageName;
+ private final String mPackageName;
private final boolean mHasPermissionToOverrideDefault;
@@ -172,8 +172,8 @@
/** Returns the display name of the app that made this request. */
@NonNull
- public String getAppPackageName() {
- return mAppPackageName;
+ public String getPackageName() {
+ return mPackageName;
}
/**
@@ -248,7 +248,7 @@
boolean isShowAllOptionsRequested) {
mToken = token;
mType = type;
- mAppPackageName = appPackageName;
+ mPackageName = appPackageName;
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault;
@@ -270,8 +270,8 @@
AnnotationValidations.validate(NonNull.class, null, mToken);
mType = type;
AnnotationValidations.validate(NonNull.class, null, mType);
- mAppPackageName = appPackageName;
- AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
+ mPackageName = appPackageName;
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = in.readBoolean();
@@ -284,7 +284,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mToken);
dest.writeString8(mType);
- dest.writeString8(mAppPackageName);
+ dest.writeString8(mPackageName);
dest.writeTypedObject(mCreateCredentialRequest, flags);
dest.writeTypedObject(mGetCredentialRequest, flags);
dest.writeBoolean(mHasPermissionToOverrideDefault);
diff --git a/core/java/android/credentials/selection/RequestToken.java b/core/java/android/credentials/selection/RequestToken.java
index 27b83f8..f1953ce 100644
--- a/core/java/android/credentials/selection/RequestToken.java
+++ b/core/java/android/credentials/selection/RequestToken.java
@@ -30,6 +30,11 @@
* To compare if two requests pertain to the same session, compare their RequestTokens using
* the {@link RequestToken#equals(Object)} method.
*
+ * For example, when receiving a {@link android.credentials.selection.CancelSelectionRequest},
+ * the developer should use {@link RequestToken#getToken()} to retrieve the token from request and
+ * compare whether it is equal with the cached token using {@link RequestToken#equals(Object)}. Only
+ * cancel the request when two tokens are the same.
+ *
* @hide
*/
@SystemApi
@@ -39,6 +44,12 @@
@NonNull
private final IBinder mToken;
+ /** @hide **/
+ @NonNull
+ public IBinder getToken() {
+ return mToken;
+ }
+
/** @hide */
@TestApi
@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED)
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 54e34ec..62473c5 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -76,6 +76,12 @@
*/
public static final int MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS = 5000;
+ /**
+ * Default value for {@link Settings.Secure#STYLUS_POINTER_ICON_ENABLED}.
+ * @hide
+ */
+ public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
+
private InputSettings() {
}
@@ -383,14 +389,19 @@
}
/**
- * Whether a pointer icon will be shown over the location of a
- * stylus pointer.
+ * Whether a pointer icon will be shown over the location of a stylus pointer.
+ *
* @hide
*/
public static boolean isStylusPointerIconEnabled(@NonNull Context context) {
+ if (InputProperties.force_enable_stylus_pointer_icon().orElse(false)) {
+ return true;
+ }
return context.getResources()
- .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
- || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+ .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
+ && Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
+ DEFAULT_STYLUS_POINTER_ICON_ENABLED, UserHandle.USER_CURRENT_OR_SELF) != 0;
}
/**
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 6c728a4..abfa4e3 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -122,3 +122,11 @@
is_fixed_read_only: true
bug: "309792384"
}
+
+flag {
+ namespace: "system_performance"
+ name: "telemetry_apis_framework_initialization"
+ description: "Control framework initialization APIs of telemetry APIs feature."
+ is_fixed_read_only: true
+ bug: "324241334"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ef2d5eb..ce7a026 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12312,6 +12312,16 @@
public static final String PRIVATE_SPACE_AUTO_LOCK = "private_space_auto_lock";
/**
+ * Toggle for enabling stylus pointer icon. Pointer icons for styluses will only be be shown
+ * when this is enabled. Enabling this alone won't enable the stylus pointer;
+ * config_enableStylusPointerIcon needs to be true as well.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String STYLUS_POINTER_ICON_ENABLED = "stylus_pointer_icon_enabled";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 1afe8d9..da8817a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -26,8 +26,8 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.ClipData;
+import android.content.Intent;
import android.content.IntentSender;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -190,7 +190,7 @@
@Nullable private final InlinePresentation mInlineTooltipPresentation;
private final IntentSender mAuthentication;
- @Nullable private final Bundle mAuthenticationExtras;
+ @Nullable private Intent mCredentialFillInIntent;
@Nullable String mId;
@@ -229,7 +229,7 @@
mInlinePresentation = inlinePresentation;
mInlineTooltipPresentation = inlineTooltipPresentation;
mAuthentication = authentication;
- mAuthenticationExtras = null;
+ mCredentialFillInIntent = null;
mId = id;
}
@@ -252,7 +252,7 @@
mInlinePresentation = dataset.mInlinePresentation;
mInlineTooltipPresentation = dataset.mInlineTooltipPresentation;
mAuthentication = dataset.mAuthentication;
- mAuthenticationExtras = dataset.mAuthenticationExtras;
+ mCredentialFillInIntent = dataset.mCredentialFillInIntent;
mId = dataset.mId;
mAutofillDatatypes = dataset.mAutofillDatatypes;
}
@@ -271,7 +271,7 @@
mInlinePresentation = builder.mInlinePresentation;
mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
mAuthentication = builder.mAuthentication;
- mAuthenticationExtras = builder.mAuthenticationExtras;
+ mCredentialFillInIntent = builder.mCredentialFillInIntent;
mId = builder.mId;
mAutofillDatatypes = builder.mAutofillDatatypes;
}
@@ -354,8 +354,14 @@
/** @hide */
@Hide
- public @Nullable Bundle getAuthenticationExtras() {
- return mAuthenticationExtras;
+ public @Nullable Intent getCredentialFillInIntent() {
+ return mCredentialFillInIntent;
+ }
+
+ /** @hide */
+ @Hide
+ public void setCredentialFillInIntent(Intent intent) {
+ mCredentialFillInIntent = intent;
}
/** @hide */
@@ -415,7 +421,7 @@
if (mAuthentication != null) {
builder.append(", hasAuthentication");
}
- if (mAuthenticationExtras != null) {
+ if (mCredentialFillInIntent != null) {
builder.append(", hasAuthenticationExtras");
}
if (mAutofillDatatypes != null) {
@@ -472,7 +478,7 @@
@Nullable private InlinePresentation mInlineTooltipPresentation;
private IntentSender mAuthentication;
- private Bundle mAuthenticationExtras;
+ private Intent mCredentialFillInIntent;
private boolean mDestroyed;
@Nullable private String mId;
@@ -655,9 +661,9 @@
* @hide
*/
@Hide
- public @NonNull Builder setAuthenticationExtras(@Nullable Bundle authenticationExtra) {
+ public @NonNull Builder setCredentialFillInIntent(@Nullable Intent credentialFillInIntent) {
throwIfDestroyed();
- mAuthenticationExtras = authenticationExtra;
+ mCredentialFillInIntent = credentialFillInIntent;
return this;
}
@@ -1401,7 +1407,7 @@
parcel.writeParcelable(mAuthentication, flags);
parcel.writeString(mId);
parcel.writeInt(mEligibleReason);
- parcel.writeTypedObject(mAuthenticationExtras, flags);
+ parcel.writeTypedObject(mCredentialFillInIntent, flags);
}
public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@@ -1437,7 +1443,7 @@
android.content.IntentSender.class);
final String datasetId = parcel.readString();
final int eligibleReason = parcel.readInt();
- final Bundle authenticationExtras = parcel.readTypedObject(Bundle.CREATOR);
+ final Intent credentialFillInIntent = parcel.readTypedObject(Intent.CREATOR);
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
@@ -1482,7 +1488,7 @@
fieldDialogPresentation);
}
builder.setAuthentication(authentication);
- builder.setAuthenticationExtras(authenticationExtras);
+ builder.setCredentialFillInIntent(credentialFillInIntent);
builder.setId(datasetId);
Dataset dataset = builder.build();
dataset.mEligibleReason = eligibleReason;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 09ec933..c43ba6c 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -56,6 +56,7 @@
* <p>See the main {@link AutofillService} documentation for more details and examples.
*/
public final class FillResponse implements Parcelable {
+ // common_typos_disable
/**
* Flag used to generate {@link FillEventHistory.Event events} of type
@@ -82,11 +83,17 @@
*/
public static final int FLAG_DELAY_FILL = 0x4;
+ /**
+ * @hide
+ */
+ public static final int FLAG_CREDENTIAL_MANAGER_RESPONSE = 0x8;
+
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_TRACK_CONTEXT_COMMITED,
FLAG_DISABLE_ACTIVITY_ONLY,
- FLAG_DELAY_FILL
+ FLAG_DELAY_FILL,
+ FLAG_CREDENTIAL_MANAGER_RESPONSE
})
@Retention(RetentionPolicy.SOURCE)
@interface FillResponseFlags {}
@@ -834,7 +841,9 @@
public Builder setFlags(@FillResponseFlags int flags) {
throwIfDestroyed();
mFlags = Preconditions.checkFlagsArgument(flags,
- FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL);
+ FLAG_TRACK_CONTEXT_COMMITED
+ | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL
+ | FLAG_CREDENTIAL_MANAGER_RESPONSE);
return this;
}
diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS
index ec44100..763c79e 100644
--- a/core/java/android/service/voice/OWNERS
+++ b/core/java/android/service/voice/OWNERS
@@ -4,4 +4,4 @@
# The owner here should not be assist owner
liangyuchen@google.com
-tuanng@google.com
+adudani@google.com
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
index 44a13c4..8fdb2c2 100644
--- a/core/java/android/service/wearable/IWearableSensingService.aidl
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -28,6 +28,7 @@
* @hide
*/
oneway interface IWearableSensingService {
+ void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
void startDetection(in AmbientContextEventRequest request, in String packageName,
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index e7e44a5..d21115b 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -17,12 +17,14 @@
package android.service.wearable;
import android.annotation.BinderThread;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.wearable.Flags;
import android.app.wearable.WearableSensingManager;
import android.content.Intent;
import android.os.Bundle;
@@ -39,6 +41,7 @@
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -94,17 +97,20 @@
return new IWearableSensingService.Stub() {
/** {@inheritDoc} */
@Override
+ public void provideSecureWearableConnection(
+ ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
+ Objects.requireNonNull(secureWearableConnection);
+ Consumer<Integer> consumer = createWearableStatusConsumer(callback);
+ WearableSensingService.this.onSecureWearableConnectionProvided(
+ secureWearableConnection, consumer);
+ }
+
+ /** {@inheritDoc} */
+ @Override
public void provideDataStream(
- ParcelFileDescriptor parcelFileDescriptor,
- RemoteCallback callback) {
+ ParcelFileDescriptor parcelFileDescriptor, RemoteCallback callback) {
Objects.requireNonNull(parcelFileDescriptor);
- Consumer<Integer> consumer = response -> {
- Bundle bundle = new Bundle();
- bundle.putInt(
- STATUS_RESPONSE_BUNDLE_KEY,
- response);
- callback.sendResult(bundle);
- };
+ Consumer<Integer> consumer = createWearableStatusConsumer(callback);
WearableSensingService.this.onDataStreamProvided(
parcelFileDescriptor, consumer);
}
@@ -116,38 +122,38 @@
SharedMemory sharedMemory,
RemoteCallback callback) {
Objects.requireNonNull(data);
- Consumer<Integer> consumer = response -> {
- Bundle bundle = new Bundle();
- bundle.putInt(
- STATUS_RESPONSE_BUNDLE_KEY,
- response);
- callback.sendResult(bundle);
- };
+ Consumer<Integer> consumer = createWearableStatusConsumer(callback);
WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
}
/** {@inheritDoc} */
@Override
- public void startDetection(@NonNull AmbientContextEventRequest request,
- String packageName, RemoteCallback detectionResultCallback,
+ public void startDetection(
+ @NonNull AmbientContextEventRequest request,
+ String packageName,
+ RemoteCallback detectionResultCallback,
RemoteCallback statusCallback) {
Objects.requireNonNull(request);
Objects.requireNonNull(packageName);
Objects.requireNonNull(detectionResultCallback);
Objects.requireNonNull(statusCallback);
- Consumer<AmbientContextDetectionResult> detectionResultConsumer = result -> {
- Bundle bundle = new Bundle();
- bundle.putParcelable(
- AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY, result);
- detectionResultCallback.sendResult(bundle);
- };
- Consumer<AmbientContextDetectionServiceStatus> statusConsumer = status -> {
- Bundle bundle = new Bundle();
- bundle.putParcelable(
- AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
- status);
- statusCallback.sendResult(bundle);
- };
+ Consumer<AmbientContextDetectionResult> detectionResultConsumer =
+ result -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
+ result);
+ detectionResultCallback.sendResult(bundle);
+ };
+ Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
+ status -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ status);
+ statusCallback.sendResult(bundle);
+ };
WearableSensingService.this.onStartDetection(
request, packageName, statusConsumer, detectionResultConsumer);
Slog.d(TAG, "startDetection " + request);
@@ -162,23 +168,26 @@
/** {@inheritDoc} */
@Override
- public void queryServiceStatus(@AmbientContextEvent.EventCode int[] eventTypes,
- String packageName, RemoteCallback callback) {
+ public void queryServiceStatus(
+ @AmbientContextEvent.EventCode int[] eventTypes,
+ String packageName,
+ RemoteCallback callback) {
Objects.requireNonNull(eventTypes);
Objects.requireNonNull(packageName);
Objects.requireNonNull(callback);
- Consumer<AmbientContextDetectionServiceStatus> consumer = response -> {
- Bundle bundle = new Bundle();
- bundle.putParcelable(
- AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY,
- response);
- callback.sendResult(bundle);
- };
+ Consumer<AmbientContextDetectionServiceStatus> consumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ response);
+ callback.sendResult(bundle);
+ };
Integer[] events = intArrayToIntegerArray(eventTypes);
WearableSensingService.this.onQueryServiceStatus(
new HashSet<>(Arrays.asList(events)), packageName, consumer);
}
-
};
}
Slog.w(TAG, "Incorrect service interface, returning null.");
@@ -186,6 +195,30 @@
}
/**
+ * Called when a secure connection to the wearable is available. See {@link
+ * WearableSensingManager#provideWearableConnection(ParcelFileDescriptor, Executor, Consumer)}
+ * for details about the secure connection.
+ *
+ * <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
+ * WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
+ * the caller of {@link WearableSensingManager#provideWearableConnection(ParcelFileDescriptor,
+ * Executor, Consumer)}.
+ *
+ * <p>The implementing class should override this method. It should return an appropriate status
+ * code via {@code statusConsumer} after receiving the {@code secureWearableConnection}.
+ *
+ * @param secureWearableConnection The secure connection to the wearable.
+ * @param statusConsumer The consumer for the service status.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
+ @BinderThread
+ public void onSecureWearableConnectionProvided(
+ @NonNull ParcelFileDescriptor secureWearableConnection,
+ @NonNull Consumer<Integer> statusConsumer) {
+ statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+ }
+
+ /**
* Called when a data stream to the wearable is provided. This data stream can be used to obtain
* data from a wearable device. It is up to the implementation to maintain the data stream and
* close the data stream when it is finished.
@@ -275,4 +308,13 @@
}
return intArray;
}
+
+ @NonNull
+ private static Consumer<Integer> createWearableStatusConsumer(RemoteCallback statusCallback) {
+ return response -> {
+ Bundle bundle = new Bundle();
+ bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
+ statusCallback.sendResult(bundle);
+ };
+ }
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7bae7ec..4ba4ee3 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1497,13 +1497,21 @@
* {@link View#SYSTEM_UI_LAYOUT_FLAGS} as well the
* {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag and fits content according
* to these flags.
- * </p>
+ *
* <p>
* If set to {@code false}, the framework will not fit the content view to the insets and will
* just pass through the {@link WindowInsets} to the content view.
- * </p>
+ *
+ * <p>
+ * If the app targets
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ * the behavior will be like setting this to {@code false}, and cannot be changed.
+ *
* @param decorFitsSystemWindows Whether the decor view should fit root-level content views for
* insets.
+ * @deprecated Make space in the container views to prevent the critical elements from getting
+ * obscured by {@link WindowInsets.Type#systemBars()} or
+ * {@link WindowInsets.Type#displayCutout()} instead.
*/
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
}
@@ -2597,7 +2605,9 @@
/**
* @return the color of the status bar.
+ * @deprecated This is no longer needed since the setter is deprecated.
*/
+ @Deprecated
@ColorInt
public abstract int getStatusBarColor();
@@ -2614,13 +2624,29 @@
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
* <p>
* The transitionName for the view background will be "android:status:background".
- * </p>
+ *
+ * <p>
+ * If the color is transparent and the window enforces the status bar contrast, the system
+ * will determine whether a scrim is necessary and draw one on behalf of the app to ensure
+ * that the status bar has enough contrast with the contents of this app, and set an appropriate
+ * effective bar background accordingly.
+ *
+ * <p>
+ * If the app targets
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ * the color will be transparent and cannot be changed.
+ *
+ * @see #setNavigationBarContrastEnforced
+ * @deprecated Draw proper background behind {@link WindowInsets.Type#statusBars()}} instead.
*/
+ @Deprecated
public abstract void setStatusBarColor(@ColorInt int color);
/**
* @return the color of the navigation bar.
+ * @deprecated This is no longer needed since the setter is deprecated.
*/
+ @Deprecated
@ColorInt
public abstract int getNavigationBarColor();
@@ -2637,9 +2663,24 @@
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
* <p>
* The transitionName for the view background will be "android:navigation:background".
- * </p>
+ *
+ * <p>
+ * If the color is transparent and the window enforces the navigation bar contrast, the system
+ * will determine whether a scrim is necessary and draw one on behalf of the app to ensure that
+ * the navigation bar has enough contrast with the contents of this app, and set an appropriate
+ * effective bar background accordingly.
+ *
+ * <p>
+ * If the app targets
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ * the color will be transparent and cannot be changed.
+ *
* @attr ref android.R.styleable#Window_navigationBarColor
+ * @see #setNavigationBarContrastEnforced
+ * @deprecated Draw proper background behind {@link WindowInsets.Type#navigationBars()} or
+ * {@link WindowInsets.Type#tappableElement()} instead.
*/
+ @Deprecated
public abstract void setNavigationBarColor(@ColorInt int color);
/**
@@ -2651,9 +2692,17 @@
* {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION} must not be set.
*
+ * <p>
+ * If the app targets
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ * the color will be transparent and cannot be changed.
+ *
* @param dividerColor The color of the thin line.
* @attr ref android.R.styleable#Window_navigationBarDividerColor
+ * @deprecated Draw proper background behind {@link WindowInsets.Type#navigationBars()} or
+ * {@link WindowInsets.Type#tappableElement()} instead.
*/
+ @Deprecated
public void setNavigationBarDividerColor(@ColorInt int dividerColor) {
}
@@ -2663,7 +2712,9 @@
* @return The color of the navigation bar divider color.
* @see #setNavigationBarColor(int)
* @attr ref android.R.styleable#Window_navigationBarDividerColor
+ * @deprecated This is no longer needed since the setter is deprecated.
*/
+ @Deprecated
public @ColorInt int getNavigationBarDividerColor() {
return 0;
}
@@ -2682,7 +2733,9 @@
* @see android.R.attr#enforceStatusBarContrast
* @see #isStatusBarContrastEnforced
* @see #setStatusBarColor
+ * @deprecated Draw proper background behind {@link WindowInsets.Type#statusBars()}} instead.
*/
+ @Deprecated
public void setStatusBarContrastEnforced(boolean ensureContrast) {
}
@@ -2697,7 +2750,9 @@
* @see android.R.attr#enforceStatusBarContrast
* @see #setStatusBarContrastEnforced
* @see #setStatusBarColor
+ * @deprecated This is not needed since the setter is deprecated.
*/
+ @Deprecated
public boolean isStatusBarContrastEnforced() {
return false;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 559ccfea7..7ebabee 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -64,6 +64,7 @@
import android.service.autofill.FillEventHistory;
import android.service.autofill.Flags;
import android.service.autofill.UserData;
+import android.service.credentials.CredentialProviderService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -2382,7 +2383,18 @@
return;
}
- final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
+ final Parcelable result;
+ if (data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT) != null) {
+ result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
+ } else if (data.getParcelableExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE) != null
+ && Flags.autofillCredmanIntegration()) {
+ result = data.getParcelableExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE);
+ } else {
+ result = null;
+ }
+
final Bundle responseData = new Bundle();
responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 1dd99ba..6a4408b 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -1,13 +1,6 @@
package: "android.view.flags"
flag {
- name: "enable_surface_native_alloc_registration"
- namespace: "toolkit"
- description: "Feature flag for registering surfaces with the VM for faster cleanup"
- bug: "306193257"
-}
-
-flag {
name: "enable_surface_native_alloc_registration_ro"
namespace: "toolkit"
description: "Feature flag for registering surfaces with the VM for faster"
diff --git a/core/java/android/view/flags/window_insets.aconfig b/core/java/android/view/flags/window_insets.aconfig
new file mode 100644
index 0000000..201b7ad
--- /dev/null
+++ b/core/java/android/view/flags/window_insets.aconfig
@@ -0,0 +1,9 @@
+package: "android.view.flags"
+
+flag {
+ name: "customizable_window_headers"
+ namespace: "lse_desktop_experience"
+ description: "Flag to control the caption bar appearance and to fit app content in its empty space"
+ bug: "316387515"
+ is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b4f9ee3..5239245 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -120,7 +120,6 @@
import android.window.ProxyOnBackInvokedDispatcher;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.IconMenuPresenter;
import com.android.internal.view.menu.ListMenuPresenter;
@@ -369,8 +368,7 @@
boolean mDecorFitsSystemWindows = true;
- @VisibleForTesting
- public final boolean mEdgeToEdgeEnforced;
+ private boolean mEdgeToEdgeEnforced;
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher;
@@ -390,11 +388,6 @@
mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(context);
mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
- mEdgeToEdgeEnforced = isEdgeToEdgeEnforced(context.getApplicationInfo(), true /* local */);
- if (mEdgeToEdgeEnforced) {
- getAttributes().privateFlags |= PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
- mDecorFitsSystemWindows = false;
- }
}
/**
@@ -436,15 +429,18 @@
*
* @param info The application to query.
* @param local Whether this is called from the process of the given application.
+ * @param windowStyle The style of the window.
* @return {@code true} if edge-to-edge is enforced. Otherwise, {@code false}.
*/
- public static boolean isEdgeToEdgeEnforced(ApplicationInfo info, boolean local) {
- return info.targetSdkVersion >= ENFORCE_EDGE_TO_EDGE_SDK_VERSION
- || (Flags.enforceEdgeToEdge() && (local
- // Calling this doesn't require a permission.
- ? CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
- // Calling this requires permissions.
- : info.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)));
+ public static boolean isEdgeToEdgeEnforced(ApplicationInfo info, boolean local,
+ TypedArray windowStyle) {
+ return !windowStyle.getBoolean(R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false)
+ && (info.targetSdkVersion >= ENFORCE_EDGE_TO_EDGE_SDK_VERSION
+ || (Flags.enforceEdgeToEdge() && (local
+ // Calling this doesn't require a permission.
+ ? CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
+ // Calling this requires permissions.
+ : info.isChangeEnabled(ENFORCE_EDGE_TO_EDGE))));
}
@Override
@@ -2470,6 +2466,13 @@
System.out.println(s);
}
+ mEdgeToEdgeEnforced = isEdgeToEdgeEnforced(
+ getContext().getApplicationInfo(), true /* local */, a);
+ if (mEdgeToEdgeEnforced) {
+ getAttributes().privateFlags |= PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
+ mDecorFitsSystemWindows = false;
+ }
+
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index a8d0d37..889434f 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -168,12 +168,12 @@
}
public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr) {
+ @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@@ -432,8 +432,14 @@
final List<MessagingMessage> newHistoricMessagingMessages =
createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
+ // Add our new MessagingMessages to groups
+ List<List<MessagingMessage>> groups = new ArrayList<>();
+ List<Person> senders = new ArrayList<>();
+ // Lets first find the groups (populate `groups` and `senders`)
+ findGroups(newHistoricMessagingMessages, newMessagingMessages, user, groups, senders);
+
return new MessagingData(user, showSpinner, unreadCount,
- newHistoricMessagingMessages, newMessagingMessages);
+ newHistoricMessagingMessages, newMessagingMessages, groups, senders);
}
/**
@@ -509,21 +515,13 @@
setUser(messagingData.getUser());
setUnreadCount(messagingData.getUnreadCount());
- List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
- List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
// Copy our groups, before they get clobbered
ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
- // Add our new MessagingMessages to groups
- List<List<MessagingMessage>> groups = new ArrayList<>();
- List<Person> senders = new ArrayList<>();
-
- // Lets first find the groups (populate `groups` and `senders`)
- findGroups(historicMessages, messages, groups, senders);
-
// Let's now create the views and reorder them accordingly
// side-effect: updates mGroups, mAddedGroups
- createGroupViews(groups, senders, messagingData.getShowSpinner());
+ createGroupViews(messagingData.getGroups(), messagingData.getSenders(),
+ messagingData.getShowSpinner());
// Let's first check which groups were removed altogether and remove them in one animation
removeGroups(oldGroups);
@@ -536,8 +534,8 @@
historicMessage.removeMessage(mToRecycle);
}
- mMessages = messages;
- mHistoricMessages = historicMessages;
+ mMessages = messagingData.getNewMessagingMessages();
+ mHistoricMessages = messagingData.getHistoricMessagingMessages();
updateHistoricMessageVisibility();
updateTitleAndNamesDisplay();
@@ -935,7 +933,7 @@
}
private void createGroupViews(List<List<MessagingMessage>> groups,
- List<Person> senders, boolean showSpinner) {
+ List<Person> senders, boolean showSpinner) {
mGroups.clear();
for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) {
List<MessagingMessage> group = groups.get(groupIndex);
@@ -983,9 +981,12 @@
}
}
+ /**
+ * Finds groups and senders from the given messaging messages and fills outGroups and outSenders
+ */
private void findGroups(List<MessagingMessage> historicMessages,
- List<MessagingMessage> messages, List<List<MessagingMessage>> groups,
- List<Person> senders) {
+ List<MessagingMessage> messages, Person user, List<List<MessagingMessage>> outGroups,
+ List<Person> outSenders) {
CharSequence currentSenderKey = null;
List<MessagingMessage> currentGroup = null;
int histSize = historicMessages.size();
@@ -1003,14 +1004,14 @@
isNewGroup |= !TextUtils.equals(key, currentSenderKey);
if (isNewGroup) {
currentGroup = new ArrayList<>();
- groups.add(currentGroup);
+ outGroups.add(currentGroup);
if (sender == null) {
- sender = mUser;
+ sender = user;
} else {
// Remove all formatting from the sender name
sender = sender.toBuilder().setName(Objects.toString(sender.getName())).build();
}
- senders.add(sender);
+ outSenders.add(sender);
currentSenderKey = key;
}
currentGroup.add(message);
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 5cda3f2..3e065bf 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -16,8 +16,8 @@
package com.android.internal.widget;
+import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import static android.app.Notification.CallStyle.DEBUG_NEW_ACTION_LAYOUT;
-import static android.app.Notification.CallStyle.USE_NEW_ACTION_LAYOUT;
import static android.text.style.DynamicDrawableSpan.ALIGN_CENTER;
import android.annotation.NonNull;
@@ -166,7 +166,7 @@
}
private void setIconToGlue(@Nullable Drawable icon) {
- if (!USE_NEW_ACTION_LAYOUT) {
+ if (!evenlyDividedCallStyleActionLayout()) {
Log.e(TAG, "glueIcon: new action layout disabled; doing nothing");
return;
}
@@ -207,7 +207,7 @@
}
private void setLabelToGlue(@Nullable CharSequence label) {
- if (!USE_NEW_ACTION_LAYOUT) {
+ if (!evenlyDividedCallStyleActionLayout()) {
Log.e(TAG, "glueLabel: new action layout disabled; doing nothing");
return;
}
@@ -255,7 +255,7 @@
return;
}
- if (!USE_NEW_ACTION_LAYOUT) {
+ if (!evenlyDividedCallStyleActionLayout()) {
Log.e(TAG, "glueIconAndLabelIfNeeded: new action layout disabled; doing nothing");
return;
}
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
index 85b0201..42de60e 100644
--- a/core/java/com/android/internal/widget/MessagingData.java
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -28,24 +28,33 @@
private final boolean mShowSpinner;
private final List<MessagingMessage> mHistoricMessagingMessages;
private final List<MessagingMessage> mNewMessagingMessages;
+ private final List<List<MessagingMessage>> mGroups;
+ private final List<Person> mSenders;
private final int mUnreadCount;
MessagingData(Person user, boolean showSpinner,
List<MessagingMessage> historicMessagingMessages,
- List<MessagingMessage> newMessagingMessages) {
+ List<MessagingMessage> newMessagingMessages, List<List<MessagingMessage>> groups,
+ List<Person> senders) {
this(user, showSpinner, /* unreadCount= */0,
- historicMessagingMessages, newMessagingMessages);
+ historicMessagingMessages, newMessagingMessages,
+ groups,
+ senders);
}
MessagingData(Person user, boolean showSpinner,
int unreadCount,
List<MessagingMessage> historicMessagingMessages,
- List<MessagingMessage> newMessagingMessages) {
+ List<MessagingMessage> newMessagingMessages,
+ List<List<MessagingMessage>> groups,
+ List<Person> senders) {
mUser = user;
mShowSpinner = showSpinner;
mUnreadCount = unreadCount;
mHistoricMessagingMessages = historicMessagingMessages;
mNewMessagingMessages = newMessagingMessages;
+ mGroups = groups;
+ mSenders = senders;
}
public Person getUser() {
@@ -67,4 +76,12 @@
public int getUnreadCount() {
return mUnreadCount;
}
+
+ public List<Person> getSenders() {
+ return mSenders;
+ }
+
+ public List<List<MessagingMessage>> getGroups() {
+ return mGroups;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index b6d7503..d000596 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -189,9 +189,15 @@
/* isHistoric= */true, usePrecomputedText);
final List<MessagingMessage> newMessagingMessages =
createMessages(newMessages, /* isHistoric */false, usePrecomputedText);
+ // Let's first find our groups!
+ List<List<MessagingMessage>> groups = new ArrayList<>();
+ List<Person> senders = new ArrayList<>();
+
+ // Lets first find the groups
+ findGroups(historicMessagingMessages, newMessagingMessages, groups, senders);
return new MessagingData(user, showSpinner,
- historicMessagingMessages, newMessagingMessages);
+ historicMessagingMessages, newMessagingMessages, groups, senders);
}
/**
@@ -256,10 +262,10 @@
private void bind(MessagingData messagingData) {
setUser(messagingData.getUser());
- List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
- List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
+ // Let's now create the views and reorder them accordingly
ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
- addMessagesToGroups(historicMessages, messages, messagingData.getShowSpinner());
+ createGroupViews(messagingData.getGroups(), messagingData.getSenders(),
+ messagingData.getShowSpinner());
// Let's first check which groups were removed altogether and remove them in one animation
removeGroups(oldGroups);
@@ -272,8 +278,8 @@
historicMessage.removeMessage(mToRecycle);
}
- mMessages = messages;
- mHistoricMessages = historicMessages;
+ mMessages = messagingData.getNewMessagingMessages();
+ mHistoricMessages = messagingData.getHistoricMessagingMessages();
updateHistoricMessageVisibility();
updateTitleAndNamesDisplay();
@@ -451,19 +457,6 @@
}
}
- private void addMessagesToGroups(List<MessagingMessage> historicMessages,
- List<MessagingMessage> messages, boolean showSpinner) {
- // Let's first find our groups!
- List<List<MessagingMessage>> groups = new ArrayList<>();
- List<Person> senders = new ArrayList<>();
-
- // Lets first find the groups
- findGroups(historicMessages, messages, groups, senders);
-
- // Let's now create the views and reorder them accordingly
- createGroupViews(groups, senders, showSpinner);
- }
-
private void createGroupViews(List<List<MessagingMessage>> groups,
List<Person> senders, boolean showSpinner) {
mGroups.clear();
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 69d2544..301dc39 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -17,7 +17,7 @@
package com.android.internal.widget;
import static android.app.Notification.CallStyle.DEBUG_NEW_ACTION_LAYOUT;
-import static android.app.Notification.CallStyle.USE_NEW_ACTION_LAYOUT;
+import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import android.annotation.DimenRes;
import android.app.Notification;
@@ -410,7 +410,7 @@
*/
@RemotableViewMethod
public void setEvenlyDividedMode(boolean evenlyDividedMode) {
- if (evenlyDividedMode && !USE_NEW_ACTION_LAYOUT) {
+ if (evenlyDividedMode && !evenlyDividedCallStyleActionLayout()) {
Log.e(TAG, "setEvenlyDividedMode(true) called with new action layout disabled; "
+ "leaving evenly divided mode disabled");
return;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 58166bf..0938ce17 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2416,6 +2416,11 @@
const std::vector<int>& fds_to_ignore,
bool is_priority_fork,
bool purge) {
+ ATRACE_CALL();
+ if (is_priority_fork) {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+ }
+
SetSignalHandlers();
// Curry a failure function.
@@ -2501,6 +2506,10 @@
// We blocked SIGCHLD prior to a fork, we unblock it here.
UnblockSignal(SIGCHLD, fail_fn);
+ if (is_priority_fork && pid != 0) {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
+ }
+
return pid;
}
@@ -2570,6 +2579,7 @@
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
+ ATRACE_CALL();
std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
fds_to_ignore(fds_to_close);
@@ -2645,6 +2655,7 @@
jintArray managed_session_socket_fds,
jboolean args_known,
jboolean is_priority_fork) {
+ ATRACE_CALL();
std::vector<int> session_socket_fds =
ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds)
.value_or(std::vector<int>());
@@ -2660,6 +2671,7 @@
bool args_known,
bool is_priority_fork,
bool purge) {
+ ATRACE_CALL();
std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
fds_to_ignore(fds_to_close);
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 104c023..10f75d0 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -612,6 +612,7 @@
}
optional Sounds sounds = 72;
+ optional SettingProto stylus_pointer_icon_enabled = 99 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Defines whether managed profile ringtones should be synced from its
// parent profile.
@@ -720,5 +721,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 99;
+ // Next tag = 100;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c71a842..5c764e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3216,6 +3216,13 @@
android:description="@string/permdesc_accessHiddenProfile"
android:protectionLevel="normal" />
+ <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile
+ users.
+ @FlaggedApi("android.multiuser.flags.enable_permission_to_access_hidden_profiles") -->
+ <permission
+ android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
<permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
android:protectionLevel="signature|role" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e7b1d09..908eeeb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2292,7 +2292,17 @@
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the status bar must not
have been requested to be translucent with
{@link android.R.attr#windowTranslucentStatus}.
- Corresponds to {@link android.view.Window#setStatusBarColor(int)}. -->
+ Corresponds to {@link android.view.Window#setStatusBarColor(int)}.
+ <p>If the color is transparent and the window enforces the status bar contrast, the
+ system will determine whether a scrim is necessary and draw one on behalf of the app to
+ ensure that the status bar has enough contrast with the contents of this app, and set
+ an appropriate effective bar background accordingly.
+ See: {@link android.R.attr#enforceStatusBarContrast}
+ <p>If the app targets
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ this attribute is ignored.
+ @deprecated Draw proper background behind
+ {@link android.view.WindowInsets.Type#statusBars()}} instead. -->
<attr name="statusBarColor" format="color" />
<!-- The color for the navigation bar. If the color is not opaque, consider setting
@@ -2302,7 +2312,18 @@
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
have been requested to be translucent with
{@link android.R.attr#windowTranslucentNavigation}.
- Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
+ Corresponds to {@link android.view.Window#setNavigationBarColor(int)}.
+ <p>If the color is transparent and the window enforces the navigation bar contrast, the
+ system will determine whether a scrim is necessary and draw one on behalf of the app to
+ ensure that the navigation bar has enough contrast with the contents of this app, and
+ set an appropriate effective bar background accordingly.
+ See: {@link android.R.attr#enforceNavigationBarContrast}
+ <p>If the app targets
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ this attribute is ignored.
+ @deprecated Draw proper background behind
+ {@link android.view.WindowInsets.Type#navigationBars()} or
+ {@link android.view.WindowInsets.Type#tappableElement()} instead. -->
<attr name="navigationBarColor" format="color" />
<!-- Shows a thin line of the specified color between the navigation bar and the app
@@ -2311,7 +2332,13 @@
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
have been requested to be translucent with
{@link android.R.attr#windowTranslucentNavigation}.
- Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}. -->
+ Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}.
+ <p>If the app targets
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above,
+ this attribute is ignored.
+ @deprecated Draw proper background behind
+ {@link android.view.WindowInsets.Type#navigationBars()} or
+ {@link android.view.WindowInsets.Type#tappableElement()} instead. -->
<attr name="navigationBarDividerColor" format="color" />
<!-- Sets whether the system should ensure that the status bar has enough
@@ -2327,7 +2354,9 @@
<p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q},
this attribute is ignored.
- @see android.view.Window#setStatusBarContrastEnforced -->
+ @see android.view.Window#setStatusBarContrastEnforced
+ @deprecated Draw proper background behind
+ {@link android.view.WindowInsets.Type#statusBars()}} instead. -->
<attr name="enforceStatusBarContrast" format="boolean" />
<!-- Sets whether the system should ensure that the navigation bar has enough
@@ -2483,6 +2512,31 @@
<!-- The icon is shown unless the launching app specified SPLASH_SCREEN_STYLE_EMPTY -->
<enum name="icon_preferred" value="1" />
</attr>
+
+ <!-- Flag indicating whether this window would opt-out the edge-to-edge enforcement.
+
+ <p>If this is false, the edge-to-edge enforcement will be applied to the window if its
+ app targets
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM VANILLA_ICE_CREAM} or above.
+ The affected behaviors are:
+ <ul>
+ <li>The framework will not fit the content view to the insets and will just pass
+ through the {@link android.view.WindowInsets} to the content view, as if calling
+ {@link android.view.Window#setDecorFitsSystemWindows(boolean)} with false.
+ <li>{@link android.view.WindowManager.LayoutParams#layoutInDisplayCutoutMode} of
+ the non-floating windows will be set to {@link
+ android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS}.
+ Changing it to other values will cause {@link lang.IllegalArgumentException}.
+ <li>The framework will set {@link android.R.attr#statusBarColor},
+ {@link android.R.attr#navigationBarColor}, and
+ {@link android.R.attr#navigationBarDividerColor} to transparent.
+ </ul>
+
+ <p>If this is true, the edge-to-edge enforcement won't be applied. However, this
+ attribute will be deprecated and disabled in a future SDK level.
+
+ <p>This is false by default. -->
+ <attr name="windowOptOutEdgeToEdgeEnforcement" format="boolean"/>
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 81a8908..4799c37 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -147,6 +147,8 @@
<public name="useBoundsForWidth"/>
<!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
<public name="autoTransact"/>
+ <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") -->
+ <public name="windowOptOutEdgeToEdgeEnforcement"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01bc0000">
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 9bb2499..61e6a36 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -127,7 +127,7 @@
<!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
visual voicemail code for Orange: 21101 -->
- <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051" />
+ <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051|33033" />
<!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf,
@@ -150,6 +150,9 @@
http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
<shortcode country="hu" pattern="[01](?:\\d{3}|\\d{9})" premium="0691227910|1784" free="116\\d{3}" />
+ <!-- Honduras -->
+ <shortcode country="hn" pattern="\\d{4,6}" free="466453" />
+
<!-- India: 1-5 digits (standard system default, not country specific) -->
<shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
@@ -171,7 +174,7 @@
<shortcode country="jp" pattern="\\d{1,5}" free="8083" />
<!-- Kenya: 5 digits, known premium codes listed -->
- <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023" />
+ <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023|24088|23054" />
<!-- Kyrgyzstan: 4 digits, known premium codes listed -->
<shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
@@ -183,7 +186,7 @@
<shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
<!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991" />
+ <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991|50976" />
<!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
<shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
@@ -195,9 +198,18 @@
<!-- Latvia: 4 digits, known premium codes listed, plus EU -->
<shortcode country="lv" pattern="\\d{4}" premium="18(?:19|63|7[1-4])" free="116\\d{3}|1399" />
+ <!-- Morocco: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="ma" pattern="\\d{1,5}" free="53819" />
+
<!-- Macedonia: 1-6 digits (not confirmed), known premium codes listed -->
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
+ <!-- Malawi: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="mw" pattern="\\d{1,5}" free="4276" />
+
+ <!-- Mozambique: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="mz" pattern="\\d{1,5}" free="1714" />
+
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="mx" pattern="\\d{4,6}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346" />
@@ -207,6 +219,9 @@
<!-- Namibia: 1-5 digits (standard system default, not country specific) -->
<shortcode country="na" pattern="\\d{1,5}" free="40005" />
+ <!-- Nicaragua -->
+ <shortcode country="ni" pattern="\\d{4,6}" free="466453" />
+
<!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
@@ -219,8 +234,8 @@
<!-- New Zealand: 3-4 digits, known premium codes listed -->
<shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|3876|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
- <!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
+ <!-- Peru: 4-6 digits (not confirmed), known premium codes listed -->
+ <shortcode country="pe" pattern="\\d{4,6}" free="9963|40778|301303" />
<!-- Philippines -->
<shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
@@ -269,6 +284,12 @@
<!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
+ <!-- Senegal(SN): 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="sn" pattern="\\d{1,5}" free="21215" />
+
+ <!-- El Salvador(SV): 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="sv" pattern="\\d{4,6}" free="466453" />
+
<!-- Taiwan -->
<shortcode country="tw" pattern="\\d{4}" free="1922" />
@@ -278,15 +299,21 @@
<!-- Tajikistan: 4 digits, known premium codes listed -->
<shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+ <!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234" />
+
<!-- Turkey -->
<shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493|3193" />
<!-- Ukraine: 4 digits, known premium codes listed -->
<shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
+ <!-- Uganda(UG): 4 digits (standard system default, not country specific) -->
+ <shortcode country="ug" pattern="\\d{4}" free="8000" />
+
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
- <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611" />
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831" />
<!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
<shortcode country="vn" pattern="\\d{1,5}" free="5001|9055" />
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index de55b07..3df3b9d2 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@@ -63,7 +64,8 @@
createPhoneWindowWithTheme(R.style.LayoutInDisplayCutoutModeUnset);
installDecor();
- if (mPhoneWindow.mEdgeToEdgeEnforced && !mPhoneWindow.isFloating()) {
+ if ((mPhoneWindow.getAttributes().privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
+ && !mPhoneWindow.isFloating()) {
assertThat(mPhoneWindow.getAttributes().layoutInDisplayCutoutMode,
is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS));
} else {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 13d38d2..9d1e507 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -124,6 +124,10 @@
<group gid="security_log_writer" />
</permission>
+ <permission name="android.permission.MANAGE_VIRTUAL_MACHINE">
+ <group gid="virtualmachine" />
+ </permission>
+
<!-- These are permissions that were mapped to gids but we need
to keep them here until an upgrade from L to the current
version is to be supported. These permissions are built-in
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index bf60944..7823277 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -108,6 +108,7 @@
<install-in user-type="FULL" />
<install-in user-type="PROFILE" />
<do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+ <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
</install-in-user-type>
<!-- Settings (Settings app) -->
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 9854e58..a80afe2 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -45,9 +45,6 @@
<!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
<bool name="config_pipEnableResizeForMenu">true</bool>
- <!-- Allow PIP to resize via dragging the corner of PiP. -->
- <bool name="config_pipEnableDragCornerResize">false</bool>
-
<!-- PiP minimum size, which is a % based off the shorter side of display width and height -->
<fraction name="config_pipShortestEdgePercent">40%</fraction>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
index 160f922..55982dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
@@ -310,12 +310,16 @@
float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top);
float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width());
float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());
- float alpha = mapRange(progress, mEnteringProgress, 1.0f);
-
+ float alpha = mapRange(progress, getPreCommitEnteringAlpha(), 1.0f);
mEnteringRect.set(left, top, left + width, top + height);
applyTransform(mEnteringTarget.leash, mEnteringRect, alpha);
}
+ private float getPreCommitEnteringAlpha() {
+ return Math.max(smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
+ MIN_WINDOW_ALPHA);
+ }
+
private float getEnteringProgress() {
return mEnteringProgress * SCALE_FACTOR;
}
@@ -325,9 +329,7 @@
if (mEnteringTarget != null && mEnteringTarget.leash != null) {
transformWithProgress(
mEnteringProgress,
- Math.max(
- smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
- MIN_WINDOW_ALPHA), /* alpha */
+ getPreCommitEnteringAlpha(),
mEnteringTarget.leash,
mEnteringRect,
-mWindowXShift,
@@ -336,6 +338,11 @@
}
}
+ private float getPreCommitLeavingAlpha() {
+ return Math.max(1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress),
+ MIN_WINDOW_ALPHA);
+ }
+
private float getLeavingProgress() {
return mLeavingProgress * SCALE_FACTOR;
}
@@ -345,9 +352,7 @@
if (mClosingTarget != null && mClosingTarget.leash != null) {
transformWithProgress(
mLeavingProgress,
- Math.max(
- 1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress),
- MIN_WINDOW_ALPHA),
+ getPreCommitLeavingAlpha(),
mClosingTarget.leash,
mClosingRect,
0,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index a2a2914..7a4ad0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -912,9 +912,8 @@
if (uid != -1) {
intent.putExtra(Settings.EXTRA_APP_UID, uid);
}
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 8b6c7b6..68d26da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -140,7 +140,7 @@
// spec takes the aspect ratio of the bounds into account, so both width and height
// scale by the same factor.
addPipExclusionBoundsChangeCallback((bounds) -> {
- mBoundsScale = Math.min((float) bounds.width() / mMaxSize.x, 1.0f);
+ updateBoundsScale();
});
}
@@ -152,6 +152,11 @@
mSizeSpecSource.onConfigurationChanged();
}
+ /** Update the bounds scale percentage value. */
+ public void updateBoundsScale() {
+ mBoundsScale = Math.min((float) mBounds.width() / mMaxSize.x, 1.0f);
+ }
+
private void reloadResources() {
mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 896ca96..e018ecc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -286,12 +286,6 @@
// For transition that we don't animate, but contains the PIP leash, we need to update the
// PIP surface, otherwise it will be reset after the transition.
if (currentPipTaskChange != null) {
- // Set the "end" bounds of pip. The default setup uses the start bounds. Since this is
- // changing the *finish*Transaction, we need to use the end bounds. This will also
- // make sure that the fade-in animation (below) uses the end bounds as well.
- if (!currentPipTaskChange.getEndAbsBounds().isEmpty()) {
- mPipBoundsState.setBounds(currentPipTaskChange.getEndAbsBounds());
- }
updatePipForUnhandledTransition(currentPipTaskChange, startTransaction,
finishTransaction);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index c5a0102..05d4f53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -244,6 +244,9 @@
// The same rotation may have been set by auto PiP-able or fixed rotation. So notify
// the change with fromRotation=false to apply the rotated destination bounds from
// PipTaskOrganizer#onMovementBoundsChanged.
+ // We need to update the bounds scale in case this was from fixed rotation, as the
+ // current proportion was computed using the previous orientation max size and is wrong.
+ mPipBoundsState.updateBoundsScale();
updateMovementBounds(null, false /* fromRotation */,
false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t);
return;
@@ -797,21 +800,15 @@
mPipBoundsAlgorithm.getMovementBounds(postChangeBounds),
mPipBoundsState.getStashedState());
- // Scale PiP on density dpi change, so it appears to be the same size physically.
- final boolean densityDpiChanged =
- mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
- && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
- != layout.densityDpi());
- if (densityDpiChanged) {
- final float scale = (float) layout.densityDpi()
- / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
- postChangeBounds.set(0, 0,
- (int) (postChangeBounds.width() * scale),
- (int) (postChangeBounds.height() * scale));
- }
-
updateDisplayLayout.run();
+ // Resize the PiP bounds to be at the same scale relative to the new size spec. For
+ // example, if PiP was resized to 90% of the maximum size on the previous layout,
+ // make sure it is 90% of the new maximum size spec.
+ postChangeBounds.set(0, 0,
+ (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()),
+ (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale()));
+
// Calculate the PiP bounds in the new orientation based on same fraction along the
// rotated movement bounds.
final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
@@ -827,6 +824,10 @@
mPipBoundsState.setHasUserResizedPip(true);
mTouchHandler.setUserResizeBounds(postChangeBounds);
+ final boolean densityDpiChanged =
+ mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
+ && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
+ != layout.densityDpi());
if (densityDpiChanged) {
// Using PipMotionHelper#movePip directly here may cause race condition since
// the app content in PiP mode may or may not be updated for the new density dpi.
@@ -1146,6 +1147,11 @@
// Update the display layout
mPipDisplayLayoutState.rotateTo(toRotation);
+ mTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
+
+ postChangeStackBounds.set(0, 0,
+ (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()),
+ (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale()));
// Calculate the stack bounds in the new orientation based on same fraction along the
// rotated movement bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index f175775..5f9195a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,19 +15,13 @@
*/
package com.android.wm.shell.pip.phone;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Looper;
import android.view.BatchedInputEventReceiver;
@@ -41,7 +35,6 @@
import androidx.annotation.VisibleForTesting;
-import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -53,7 +46,6 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
-import java.util.function.Function;
/**
* Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -77,7 +69,6 @@
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
private final int mDisplayId;
private final ShellExecutor mMainExecutor;
- private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
private final PointF mDownSecondPoint = new PointF();
@@ -88,24 +79,15 @@
private final Rect mLastResizeBounds = new Rect();
private final Rect mUserResizeBounds = new Rect();
private final Rect mDownBounds = new Rect();
- private final Rect mDragCornerSize = new Rect();
- private final Rect mTmpTopLeftCorner = new Rect();
- private final Rect mTmpTopRightCorner = new Rect();
- private final Rect mTmpBottomLeftCorner = new Rect();
- private final Rect mTmpBottomRightCorner = new Rect();
- private final Rect mDisplayBounds = new Rect();
- private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
private final Consumer<Rect> mUpdateResizeBoundsCallback;
- private int mDelta;
private float mTouchSlop;
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnablePinchResize;
- private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mOngoingPinchToResize = false;
@@ -123,7 +105,7 @@
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
PipDismissTargetHandler pipDismissTargetHandler,
- Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
+ Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
ShellExecutor mainExecutor) {
mContext = context;
@@ -135,7 +117,6 @@
mPipTouchState = pipTouchState;
mPipTaskOrganizer = pipTaskOrganizer;
mPipDismissTargetHandler = pipDismissTargetHandler;
- mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -171,20 +152,9 @@
}
private void reloadResources() {
- final Resources res = mContext.getResources();
- mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
- mEnableDragCornerResize = res.getBoolean(R.bool.config_pipEnableDragCornerResize);
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
}
- private void resetDragCorners() {
- mDragCornerSize.set(0, 0, mDelta, mDelta);
- mTmpTopLeftCorner.set(mDragCornerSize);
- mTmpTopRightCorner.set(mDragCornerSize);
- mTmpBottomLeftCorner.set(mDragCornerSize);
- mTmpBottomRightCorner.set(mDragCornerSize);
- }
-
private void disposeInputChannel() {
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
@@ -232,7 +202,7 @@
@VisibleForTesting
void onInputEvent(InputEvent ev) {
- if (!mEnableDragCornerResize && !mEnablePinchResize) {
+ if (!mEnablePinchResize) {
// No need to handle anything if neither form of resizing is enabled.
return;
}
@@ -260,8 +230,6 @@
if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize(mv);
- } else if (mEnableDragCornerResize) {
- onDragCornerResize(mv);
}
}
}
@@ -273,48 +241,6 @@
return mCtrlType != CTRL_NONE || mOngoingPinchToResize;
}
- /**
- * Check whether the current x,y coordinate is within the region in which drag-resize should
- * start.
- * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
- * overlaps with the PIP window while the rest goes outside of the PIP window.
- * _ _ _ _
- * |_|_|_________|_|_|
- * |_|_| |_|_|
- * | PIP |
- * | WINDOW |
- * _|_ _|_
- * |_|_|_________|_|_|
- * |_|_| |_|_|
- */
- public boolean isWithinDragResizeRegion(int x, int y) {
- if (!mEnableDragCornerResize) {
- return false;
- }
-
- final Rect currentPipBounds = mPipBoundsState.getBounds();
- if (currentPipBounds == null) {
- return false;
- }
- resetDragCorners();
- mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2,
- currentPipBounds.top - mDelta / 2);
- mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2,
- currentPipBounds.top - mDelta / 2);
- mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2,
- currentPipBounds.bottom - mDelta / 2);
- mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2,
- currentPipBounds.bottom - mDelta / 2);
-
- mTmpRegion.setEmpty();
- mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION);
- mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION);
- mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION);
- mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION);
-
- return mTmpRegion.contains(x, y);
- }
-
public boolean isUsingPinchToZoom() {
return mEnablePinchResize;
}
@@ -325,62 +251,17 @@
public boolean willStartResizeGesture(MotionEvent ev) {
if (isInValidSysUiState()) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
- return true;
- }
- break;
-
- case MotionEvent.ACTION_POINTER_DOWN:
- if (mEnablePinchResize && ev.getPointerCount() == 2) {
- onPinchResize(ev);
- mOngoingPinchToResize = mAllowGesture;
- return mAllowGesture;
- }
- break;
-
- default:
- break;
+ if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+ if (mEnablePinchResize && ev.getPointerCount() == 2) {
+ onPinchResize(ev);
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
+ }
}
}
return false;
}
- private void setCtrlType(int x, int y) {
- final Rect currentPipBounds = mPipBoundsState.getBounds();
-
- Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
-
- mDisplayBounds.set(movementBounds.left,
- movementBounds.top,
- movementBounds.right + currentPipBounds.width(),
- movementBounds.bottom + currentPipBounds.height());
-
- if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
- && currentPipBounds.left != mDisplayBounds.left) {
- mCtrlType |= CTRL_LEFT;
- mCtrlType |= CTRL_TOP;
- }
- if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
- && currentPipBounds.right != mDisplayBounds.right) {
- mCtrlType |= CTRL_RIGHT;
- mCtrlType |= CTRL_TOP;
- }
- if (mTmpBottomRightCorner.contains(x, y)
- && currentPipBounds.bottom != mDisplayBounds.bottom
- && currentPipBounds.right != mDisplayBounds.right) {
- mCtrlType |= CTRL_RIGHT;
- mCtrlType |= CTRL_BOTTOM;
- }
- if (mTmpBottomLeftCorner.contains(x, y)
- && currentPipBounds.bottom != mDisplayBounds.bottom
- && currentPipBounds.left != mDisplayBounds.left) {
- mCtrlType |= CTRL_LEFT;
- mCtrlType |= CTRL_BOTTOM;
- }
- }
-
private boolean isInValidSysUiState() {
return mIsSysUiStateValid;
}
@@ -457,59 +338,6 @@
}
}
- private void onDragCornerResize(MotionEvent ev) {
- int action = ev.getActionMasked();
- float x = ev.getX();
- float y = ev.getY() - mOhmOffset;
- if (action == MotionEvent.ACTION_DOWN) {
- mLastResizeBounds.setEmpty();
- mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
- if (mAllowGesture) {
- setCtrlType((int) x, (int) y);
- mDownPoint.set(x, y);
- mDownBounds.set(mPipBoundsState.getBounds());
- }
- } else if (mAllowGesture) {
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN:
- // We do not support multi touch for resizing via drag
- mAllowGesture = false;
- break;
- case MotionEvent.ACTION_MOVE:
- // Capture inputs
- if (!mThresholdCrossed
- && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
- mThresholdCrossed = true;
- // Reset the down to begin resizing from this point
- mDownPoint.set(x, y);
- mInputMonitor.pilferPointers();
- }
- if (mThresholdCrossed) {
- if (mPhonePipMenuController.isMenuVisible()) {
- mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE,
- false /* resize */);
- }
- final Rect currentPipBounds = mPipBoundsState.getBounds();
- mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
- mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
- mMinSize.y, mMaxSize, true,
- mDownBounds.width() > mDownBounds.height()));
- mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
- mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
- true /* useCurrentSize */);
- mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
- null);
- mPipBoundsState.setHasUserResizedPip(true);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- finishResize();
- break;
- }
- }
- }
-
private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
final int leftEdge = bounds.left;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 81705e2..11c356d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -212,7 +212,7 @@
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
- this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
+ this::updateMovementBounds, pipUiEventLogger,
menuController, mainExecutor);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index a666e20..bfb60c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -253,7 +253,7 @@
: taskInfo.topActivityInfo;
params.layoutInDisplayCutoutMode = a.getInt(
R.styleable.Window_windowLayoutInDisplayCutoutMode,
- PhoneWindow.isEdgeToEdgeEnforced(activityInfo.applicationInfo, false /* local */)
+ PhoneWindow.isEdgeToEdgeEnforced(activityInfo.applicationInfo, false /* local */, a)
? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
: params.layoutInDisplayCutoutMode);
params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 8207b85..07cd682 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -86,6 +86,8 @@
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
+
+ pipApp.tapPipToShowMenu(wmHelper)
}
}
@@ -194,6 +196,16 @@
}
}
+ @Postsubmit
+ @Test
+ fun menuOverlayMatchesTaskSurface() {
+ flicker.assertLayersEnd {
+ val pipAppRegion = visibleRegion(pipApp)
+ val pipMenuRegion = visibleRegion(ComponentNameMatcher.PIP_MENU_OVERLAY)
+ pipAppRegion.coversExactly(pipMenuRegion.region)
+ }
+ }
+
/** {@inheritDoc} */
@FlakyTest(bugId = 267424412)
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 9719ba8..cc726cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -121,7 +121,7 @@
mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
mPipDismissTargetHandler,
- (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+ () -> {}, mPipUiEventLogger, mPhonePipMenuController,
mMainExecutor) {
@Override
public void pilferPointers() {
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 40b0e3e..4f1a8ee 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -18,6 +18,7 @@
import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
+import android.annotation.DurationMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -112,17 +113,11 @@
*/
public static final int FADE_STATE_ENABLED_DEFAULT = 1;
- /**
- * Defines the enabled state with Automotive specific configurations
- */
- public static final int FADE_STATE_ENABLED_AUTO = 2;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = false, prefix = "FADE_STATE", value = {
FADE_STATE_DISABLED,
FADE_STATE_ENABLED_DEFAULT,
- FADE_STATE_ENABLED_AUTO,
})
public @interface FadeStateEnum {}
@@ -143,7 +138,14 @@
* @see #getFadeOutDurationForAudioAttributes(AudioAttributes)
* @see #getFadeInDurationForAudioAttributes(AudioAttributes)
*/
- public static final long DURATION_NOT_SET = 0;
+ public static final @DurationMillisLong long DURATION_NOT_SET = 0;
+
+ /** Defines the default fade out duration */
+ private static final @DurationMillisLong long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
+
+ /** Defines the default fade in duration */
+ private static final @DurationMillisLong long DEFAULT_FADE_IN_DURATION_MS = 1_000;
+
/** Map of Usage to Fade volume shaper configs wrapper */
private final SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap;
/** Map of AudioAttributes to Fade volume shaper configs wrapper */
@@ -161,14 +163,15 @@
/** fade state */
private final @FadeStateEnum int mFadeState;
/** fade out duration from builder - used for creating default fade out volume shaper */
- private final long mFadeOutDurationMillis;
+ private final @DurationMillisLong long mFadeOutDurationMillis;
/** fade in duration from builder - used for creating default fade in volume shaper */
- private final long mFadeInDurationMillis;
+ private final @DurationMillisLong long mFadeInDurationMillis;
/** delay after which the offending players are faded back in */
- private final long mFadeInDelayForOffendersMillis;
+ private final @DurationMillisLong long mFadeInDelayForOffendersMillis;
- private FadeManagerConfiguration(int fadeState, long fadeOutDurationMillis,
- long fadeInDurationMillis, long offendersFadeInDelayMillis,
+ private FadeManagerConfiguration(int fadeState, @DurationMillisLong long fadeOutDurationMillis,
+ @DurationMillisLong long fadeInDurationMillis,
+ @DurationMillisLong long offendersFadeInDelayMillis,
@NonNull SparseArray<FadeVolumeShaperConfigsWrapper> usageToFadeWrapperMap,
@NonNull ArrayMap<AudioAttributes, FadeVolumeShaperConfigsWrapper> attrToFadeWrapperMap,
@NonNull IntArray fadeableUsages, @NonNull IntArray unfadeableContentTypes,
@@ -196,8 +199,6 @@
/**
* Get the fade state
- *
- * @return one of the {@link FadeStateEnum} state
*/
@FadeStateEnum
public int getFadeState() {
@@ -207,7 +208,7 @@
/**
* Get the list of usages that can be faded
*
- * @return list of {@link android.media.AudioAttributes.AttributeUsage} that shall be faded
+ * @return list of {@link android.media.AudioAttributes usages} that shall be faded
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@NonNull
@@ -217,10 +218,10 @@
}
/**
- * Get the list of {@link android.media.AudioPlaybackConfiguration.PlayerType player types}
- * that cannot be faded
+ * Get the list of {@link android.media.AudioPlaybackConfiguration player types} that can be
+ * faded
*
- * @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType}
+ * @return list of {@link android.media.AudioPlaybackConfiguration player types}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@NonNull
@@ -230,10 +231,9 @@
}
/**
- * Get the list of {@link android.media.AudioAttributes.AttributeContentType content types}
- * that cannot be faded
+ * Get the list of {@link android.media.AudioAttributes content types} that can be faded
*
- * @return list of {@link android.media.AudioAttributes.AttributeContentType}
+ * @return list of {@link android.media.AudioAttributes content types}
* @throws IllegalStateExceptionif if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@NonNull
@@ -267,15 +267,15 @@
}
/**
- * Get the duration used to fade out players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * Get the duration used to fade out players with {@link android.media.AudioAttributes usage}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usage the {@link android.media.AudioAttributes usage}
* @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- public long getFadeOutDurationForUsage(int usage) {
+ @DurationMillisLong
+ public long getFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -283,15 +283,15 @@
}
/**
- * Get the duration used to fade in players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * Get the duration used to fade in players with {@link android.media.AudioAttributes usage}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usage the {@link android.media.AudioAttributes usage}
* @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- public long getFadeInDurationForUsage(int usage) {
+ @DurationMillisLong
+ public long getFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -300,16 +300,17 @@
/**
* Get the {@link android.media.VolumeShaper.Configuration} used to fade out players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usage the {@link android.media.AudioAttributes usage}
* @return {@link android.media.VolumeShaper.Configuration} if set for the usage or
- * {@code null} otherwise
+ * {@code null} otherwise
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@Nullable
- public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int usage) {
+ public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(
+ @AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage),
@@ -318,16 +319,17 @@
/**
* Get the {@link android.media.VolumeShaper.Configuration} used to fade in players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of player
+ * @param usage the {@link android.media.AudioAttributes usage}
* @return {@link android.media.VolumeShaper.Configuration} if set for the usage or
- * {@code null} otherwise
+ * {@code null} otherwise
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@Nullable
- public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int usage) {
+ public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(
+ @AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage),
@@ -339,10 +341,11 @@
*
* @param audioAttributes {@link android.media.AudioAttributes}
* @return duration in milliseconds if set for the audio attributes or
- * {@link #DURATION_NOT_SET} otherwise
+ * {@link #DURATION_NOT_SET} otherwise
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
+ @DurationMillisLong
public long getFadeOutDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -354,10 +357,11 @@
*
* @param audioAttributes {@link android.media.AudioAttributes}
* @return duration in milliseconds if set for the audio attributes or
- * {@link #DURATION_NOT_SET} otherwise
+ * {@link #DURATION_NOT_SET} otherwise
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
+ @DurationMillisLong
public long getFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -370,7 +374,7 @@
*
* @param audioAttributes {@link android.media.AudioAttributes}
* @return {@link android.media.VolumeShaper.Configuration} if set for the audio attribute or
- * {@code null} otherwise
+ * {@code null} otherwise
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@@ -389,7 +393,7 @@
*
* @param audioAttributes {@link android.media.AudioAttributes}
* @return {@link android.media.VolumeShaper.Configuration} used for fading in if set for the
- * audio attribute or {@code null} otherwise
+ * audio attribute or {@code null} otherwise
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
@@ -407,7 +411,7 @@
* configurations are defined
*
* @return list of {@link android.media.AudioAttributes} with valid volume shaper configs or
- * empty list if none set.
+ * empty list if none set.
*/
@NonNull
public List<AudioAttributes> getAudioAttributesWithVolumeShaperConfigs() {
@@ -417,8 +421,14 @@
/**
* Get the delay after which the offending players are faded back in
*
+ * Players are categorized as offending if they do not honor audio focus state changes. For
+ * example - when an app loses audio focus, it is expected that the app stops any active
+ * player in favor of the app(s) that gained audio focus. However, if the app do not stop the
+ * audio playback, such players are termed as offenders.
+ *
* @return delay in milliseconds
*/
+ @DurationMillisLong
public long getFadeInDelayForOffenders() {
return mFadeInDelayForOffendersMillis;
}
@@ -435,8 +445,9 @@
/**
* Query if the usage is fadeable
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
- * @return {@code true} if usage is fadeable, {@code false} otherwise
+ * @param usage the {@link android.media.AudioAttributes usage}
+ * @return {@code true} if usage is fadeable, {@code false} when the fade state is set to
+ * {@link #FADE_STATE_DISABLED} or if the usage is not fadeable.
*/
public boolean isUsageFadeable(@AudioAttributes.AttributeUsage int usage) {
if (!isFadeEnabled()) {
@@ -448,9 +459,9 @@
/**
* Query if the content type is unfadeable
*
- * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+ * @param contentType the {@link android.media.AudioAttributes content type}
* @return {@code true} if content type is unfadeable or if fade state is set to
- * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+ * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
*/
public boolean isContentTypeUnfadeable(@AudioAttributes.AttributeContentType int contentType) {
if (!isFadeEnabled()) {
@@ -462,11 +473,11 @@
/**
* Query if the player type is unfadeable
*
- * @param playerType the {@link android.media.AudioPlaybackConfiguration} player type
+ * @param playerType the {@link android.media.AudioPlaybackConfiguration player type}
* @return {@code true} if player type is unfadeable or if fade state is set to
- * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+ * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
*/
- public boolean isPlayerTypeUnfadeable(int playerType) {
+ public boolean isPlayerTypeUnfadeable(@AudioPlaybackConfiguration.PlayerType int playerType) {
if (!isFadeEnabled()) {
return true;
}
@@ -478,7 +489,7 @@
*
* @param audioAttributes the {@link android.media.AudioAttributes}
* @return {@code true} if audio attributes is unfadeable or if fade state is set to
- * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+ * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
* @throws NullPointerException if the audio attributes is {@code null}
*/
public boolean isAudioAttributesUnfadeable(@NonNull AudioAttributes audioAttributes) {
@@ -494,7 +505,7 @@
*
* @param uid the uid of application
* @return {@code true} if uid is unfadeable or if fade state is set to
- * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+ * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
*/
public boolean isUidUnfadeable(int uid) {
if (!isFadeEnabled()) {
@@ -503,6 +514,20 @@
return mUnfadeableUids.contains(uid);
}
+ /**
+ * Returns the default fade out duration (in milliseconds)
+ */
+ public static @DurationMillisLong long getDefaultFadeOutDurationMillis() {
+ return DEFAULT_FADE_OUT_DURATION_MS;
+ }
+
+ /**
+ * Returns the default fade in duration (in milliseconds)
+ */
+ public static @DurationMillisLong long getDefaultFadeInDurationMillis() {
+ return DEFAULT_FADE_IN_DURATION_MS;
+ }
+
@Override
public String toString() {
return "FadeManagerConfiguration { fade state = " + fadeStateToString(mFadeState)
@@ -520,7 +545,7 @@
/**
* Convert fade state into a human-readable string
*
- * @param fadeState one of the fade state in {@link FadeStateEnum}
+ * @param fadeState one of {@link #FADE_STATE_DISABLED} or {@link #FADE_STATE_ENABLED_DEFAULT}
* @return human-readable string
* @hide
*/
@@ -531,8 +556,6 @@
return "FADE_STATE_DISABLED";
case FADE_STATE_ENABLED_DEFAULT:
return "FADE_STATE_ENABLED_DEFAULT";
- case FADE_STATE_ENABLED_AUTO:
- return "FADE_STATE_ENABLED_AUTO";
default:
return "unknown fade state: " + fadeState;
}
@@ -712,9 +735,9 @@
*
* <p><b>Notes:</b>
* <ul>
- * <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT} or
- * {@link #FADE_STATE_ENABLED_AUTO}, the builder expects at least one valid usage to be
- * set/added. Failure to do so will result in an exception during {@link #build()}</li>
+ * <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT}, the builder expects at
+ * least one valid usage to be set/added. Failure to do so will result in an exception
+ * during {@link #build()}</li>
* <li>Every usage added to the fadeable list should have corresponding volume shaper
* configs defined. This can be achieved by setting either the duration or volume shaper
* config through {@link #setFadeOutDurationForUsage(int, long)} or
@@ -741,11 +764,6 @@
private static final long IS_FADEABLE_USAGES_FIELD_SET = 1 << 1;
private static final long IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET = 1 << 2;
- /** duration of the fade out curve */
- private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
- /** duration of the fade in curve */
- private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000;
-
/**
* delay after which a faded out player will be faded back in. This will be heard by the
* user only in the case of unmuting players that didn't respect audio focus and didn't
@@ -771,9 +789,10 @@
});
private int mFadeState = FADE_STATE_ENABLED_DEFAULT;
- private long mFadeInDelayForOffendersMillis = DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
- private long mFadeOutDurationMillis;
- private long mFadeInDurationMillis;
+ private @DurationMillisLong long mFadeInDelayForOffendersMillis =
+ DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
+ private @DurationMillisLong long mFadeOutDurationMillis;
+ private @DurationMillisLong long mFadeInDurationMillis;
private long mBuilderFieldsSet;
private SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap =
new SparseArray<>();
@@ -787,7 +806,8 @@
private List<AudioAttributes> mUnfadeableAudioAttributes = new ArrayList<>();
/**
- * Constructs a new Builder with default fade out and fade in durations
+ * Constructs a new Builder with {@link #DEFAULT_FADE_OUT_DURATION_MS} and
+ * {@link #DEFAULT_FADE_IN_DURATION_MS} durations.
*/
public Builder() {
mFadeOutDurationMillis = DEFAULT_FADE_OUT_DURATION_MS;
@@ -800,7 +820,8 @@
* @param fadeOutDurationMillis duration in milliseconds used for fading out
* @param fadeInDurationMills duration in milliseconds used for fading in
*/
- public Builder(long fadeOutDurationMillis, long fadeInDurationMills) {
+ public Builder(@DurationMillisLong long fadeOutDurationMillis,
+ @DurationMillisLong long fadeInDurationMills) {
mFadeOutDurationMillis = fadeOutDurationMillis;
mFadeInDurationMillis = fadeInDurationMills;
}
@@ -830,7 +851,8 @@
/**
* Set the overall fade state
*
- * @param state one of the {@link FadeStateEnum} states
+ * @param state one of the {@link #FADE_STATE_DISABLED} or
+ * {@link #FADE_STATE_ENABLED_DEFAULT} states
* @return the same Builder instance
* @throws IllegalArgumentException if the fade state is invalid
* @see #getFadeState()
@@ -844,21 +866,22 @@
/**
* Set the {@link android.media.VolumeShaper.Configuration} used to fade out players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
* <p>
* This method accepts {@code null} for volume shaper config to clear a previously set
* configuration (example, if set through
* {@link #Builder(android.media.FadeManagerConfiguration)})
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+ * @param usage the {@link android.media.AudioAttributes usage} of target player
* @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used
- * to fade out players with usage
+ * to fade out players with usage
* @return the same Builder instance
* @throws IllegalArgumentException if the usage is invalid
* @see #getFadeOutVolumeShaperConfigForUsage(int)
*/
@NonNull
- public Builder setFadeOutVolumeShaperConfigForUsage(int usage,
+ public Builder setFadeOutVolumeShaperConfigForUsage(
+ @AudioAttributes.AttributeUsage int usage,
@Nullable VolumeShaper.Configuration fadeOutVShaperConfig) {
validateUsage(usage);
getFadeVolShaperConfigWrapperForUsage(usage)
@@ -869,21 +892,22 @@
/**
* Set the {@link android.media.VolumeShaper.Configuration} used to fade in players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
* <p>
* This method accepts {@code null} for volume shaper config to clear a previously set
* configuration (example, if set through
* {@link #Builder(android.media.FadeManagerConfiguration)})
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usage the {@link android.media.AudioAttributes usage}
* @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used
- * to fade in players with usage
+ * to fade in players with usage
* @return the same Builder instance
* @throws IllegalArgumentException if the usage is invalid
* @see #getFadeInVolumeShaperConfigForUsage(int)
*/
@NonNull
- public Builder setFadeInVolumeShaperConfigForUsage(int usage,
+ public Builder setFadeInVolumeShaperConfigForUsage(
+ @AudioAttributes.AttributeUsage int usage,
@Nullable VolumeShaper.Configuration fadeInVShaperConfig) {
validateUsage(usage);
getFadeVolShaperConfigWrapperForUsage(usage)
@@ -894,7 +918,7 @@
/**
* Set the duration used for fading out players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
* <p>
* A Volume shaper configuration is generated with the provided duration and default
* volume curve definitions. This config is then used to fade out players with given usage.
@@ -904,17 +928,18 @@
* {@link #DURATION_NOT_SET} and sets the corresponding fade out volume shaper config to
* {@code null}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+ * @param usage the {@link android.media.AudioAttributes usage} of target player
* @param fadeOutDurationMillis positive duration in milliseconds or
- * {@link #DURATION_NOT_SET}
+ * {@link #DURATION_NOT_SET}
* @return the same Builder instance
* @throws IllegalArgumentException if the fade out duration is non-positive with the
- * exception of {@link #DURATION_NOT_SET}
+ * exception of {@link #DURATION_NOT_SET}
* @see #setFadeOutVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)
* @see #getFadeOutDurationForUsage(int)
*/
@NonNull
- public Builder setFadeOutDurationForUsage(int usage, long fadeOutDurationMillis) {
+ public Builder setFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage,
+ @DurationMillisLong long fadeOutDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -924,7 +949,7 @@
/**
* Set the duration used for fading in players with
- * {@link android.media.AudioAttributes.AttributeUsage}
+ * {@link android.media.AudioAttributes usage}
* <p>
* A Volume shaper configuration is generated with the provided duration and default
* volume curve definitions. This config is then used to fade in players with given usage.
@@ -934,17 +959,18 @@
* {@link #DURATION_NOT_SET} and sets the corresponding fade in volume shaper config to
* {@code null}
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+ * @param usage the {@link android.media.AudioAttributes usage} of target player
* @param fadeInDurationMillis positive duration in milliseconds or
- * {@link #DURATION_NOT_SET}
+ * {@link #DURATION_NOT_SET}
* @return the same Builder instance
* @throws IllegalArgumentException if the fade in duration is non-positive with the
- * exception of {@link #DURATION_NOT_SET}
+ * exception of {@link #DURATION_NOT_SET}
* @see #setFadeInVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)
* @see #getFadeInDurationForUsage(int)
*/
@NonNull
- public Builder setFadeInDurationForUsage(int usage, long fadeInDurationMillis) {
+ public Builder setFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage,
+ @DurationMillisLong long fadeInDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -962,9 +988,8 @@
*
* @param audioAttributes the {@link android.media.AudioAttributes}
* @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to
- * fade out players with audio attribute
+ * fade out players with audio attribute
* @return the same Builder instance
- * @throws NullPointerException if the audio attributes is {@code null}
* @see #getFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes)
*/
@NonNull
@@ -988,7 +1013,7 @@
*
* @param audioAttributes the {@link android.media.AudioAttributes}
* @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to
- * fade in players with audio attribute
+ * fade in players with audio attribute
* @return the same Builder instance
* @throws NullPointerException if the audio attributes is {@code null}
* @see #getFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes)
@@ -1017,12 +1042,12 @@
* {@code null}
*
* @param audioAttributes the {@link android.media.AudioAttributes} for which the fade out
- * duration will be set/updated/reset
+ * duration will be set/updated/reset
* @param fadeOutDurationMillis positive duration in milliseconds or
- * {@link #DURATION_NOT_SET}
+ * {@link #DURATION_NOT_SET}
* @return the same Builder instance
* @throws IllegalArgumentException if the fade out duration is non-positive with the
- * exception of {@link #DURATION_NOT_SET}
+ * exception of {@link #DURATION_NOT_SET}
* @see #getFadeOutDurationForAudioAttributes(AudioAttributes)
* @see #setFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes,
* VolumeShaper.Configuration)
@@ -1030,7 +1055,7 @@
@NonNull
public Builder setFadeOutDurationForAudioAttributes(
@NonNull AudioAttributes audioAttributes,
- long fadeOutDurationMillis) {
+ @DurationMillisLong long fadeOutDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -1039,8 +1064,7 @@
}
/**
- * Set the duration used for fading in players of type
- * {@link android.media.AudioAttributes}.
+ * Set the duration used for fading in players of type {@link android.media.AudioAttributes}
* <p>
* A Volume shaper configuration is generated with the provided duration and default
* volume curve definitions. This config is then used to fade in players with given usage.
@@ -1051,19 +1075,19 @@
* {@code null}
*
* @param audioAttributes the {@link android.media.AudioAttributes} for which the fade in
- * duration will be set/updated/reset
+ * duration will be set/updated/reset
* @param fadeInDurationMillis positive duration in milliseconds or
- * {@link #DURATION_NOT_SET}
+ * {@link #DURATION_NOT_SET}
* @return the same Builder instance
* @throws IllegalArgumentException if the fade in duration is non-positive with the
- * exception of {@link #DURATION_NOT_SET}
+ * exception of {@link #DURATION_NOT_SET}
* @see #getFadeInDurationForAudioAttributes(AudioAttributes)
* @see #setFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes,
* VolumeShaper.Configuration)
*/
@NonNull
public Builder setFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes,
- long fadeInDurationMillis) {
+ @DurationMillisLong long fadeInDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1072,22 +1096,18 @@
}
/**
- * Set the list of {@link android.media.AudioAttributes.AttributeUsage} that can be faded
+ * Set the list of {@link android.media.AudioAttributes usage} that can be faded
*
* <p>This is a positive list. Players with matching usage will be considered for fading.
* Usages that are not part of this list will not be faded
*
- * <p>Passing an empty list as input clears the existing list. This can be used to
- * reset the list when using a copy constructor
- *
* <p><b>Warning:</b> When fade state is set to enabled, the builder expects at least one
* usage to be set/added. Failure to do so will result in an exception during
* {@link #build()}
*
- * @param usages List of the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usages List of the {@link android.media.AudioAttributes usages}
* @return the same Builder instance
* @throws IllegalArgumentException if the usages are invalid
- * @throws NullPointerException if the usage list is {@code null}
* @see #getFadeableUsages()
*/
@NonNull
@@ -1101,9 +1121,9 @@
}
/**
- * Add the {@link android.media.AudioAttributes.AttributeUsage} to the fadeable list
+ * Add the {@link android.media.AudioAttributes usage} to the fadeable list
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * @param usage the {@link android.media.AudioAttributes usage}
* @return the same Builder instance
* @throws IllegalArgumentException if the usage is invalid
* @see #getFadeableUsages()
@@ -1120,30 +1140,23 @@
}
/**
- * Remove the {@link android.media.AudioAttributes.AttributeUsage} from the fadeable list
- * <p>
- * Players of this usage type will not be faded.
+ * Clears the fadeable {@link android.media.AudioAttributes usage} list
*
- * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+ * <p>This can be used to reset the list when using a copy constructor
+ *
* @return the same Builder instance
- * @throws IllegalArgumentException if the usage is invalid
* @see #getFadeableUsages()
* @see #setFadeableUsages(List)
*/
@NonNull
- public Builder clearFadeableUsage(@AudioAttributes.AttributeUsage int usage) {
- validateUsage(usage);
+ public Builder clearFadeableUsages() {
setFlag(IS_FADEABLE_USAGES_FIELD_SET);
- int index = mFadeableUsages.indexOf(usage);
- if (index != INVALID_INDEX) {
- mFadeableUsages.remove(index);
- }
+ mFadeableUsages.clear();
return this;
}
/**
- * Set the list of {@link android.media.AudioAttributes.AttributeContentType} that can not
- * be faded
+ * Set the list of {@link android.media.AudioAttributes content type} that can not be faded
*
* <p>This is a negative list. Players with matching content type of this list will not be
* faded. Content types that are not part of this list will be considered for fading.
@@ -1151,10 +1164,9 @@
* <p>Passing an empty list as input clears the existing list. This can be used to
* reset the list when using a copy constructor
*
- * @param contentTypes list of {@link android.media.AudioAttributes.AttributeContentType}
+ * @param contentTypes list of {@link android.media.AudioAttributes content types}
* @return the same Builder instance
* @throws IllegalArgumentException if the content types are invalid
- * @throws NullPointerException if the content type list is {@code null}
* @see #getUnfadeableContentTypes()
*/
@NonNull
@@ -1168,9 +1180,9 @@
}
/**
- * Add the {@link android.media.AudioAttributes.AttributeContentType} to unfadeable list
+ * Add the {@link android.media.AudioAttributes content type} to unfadeable list
*
- * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+ * @param contentType the {@link android.media.AudioAttributes content type}
* @return the same Builder instance
* @throws IllegalArgumentException if the content type is invalid
* @see #setUnfadeableContentTypes(List)
@@ -1188,24 +1200,18 @@
}
/**
- * Remove the {@link android.media.AudioAttributes.AttributeContentType} from the
- * unfadeable list
+ * Clears the unfadeable {@link android.media.AudioAttributes content type} list
*
- * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+ * <p>This can be used to reset the list when using a copy constructor
+ *
* @return the same Builder instance
- * @throws IllegalArgumentException if the content type is invalid
* @see #setUnfadeableContentTypes(List)
* @see #getUnfadeableContentTypes()
*/
@NonNull
- public Builder clearUnfadeableContentType(
- @AudioAttributes.AttributeContentType int contentType) {
- validateContentType(contentType);
+ public Builder clearUnfadeableContentTypes() {
setFlag(IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET);
- int index = mUnfadeableContentTypes.indexOf(contentType);
- if (index != INVALID_INDEX) {
- mUnfadeableContentTypes.remove(index);
- }
+ mUnfadeableContentTypes.clear();
return this;
}
@@ -1213,14 +1219,10 @@
* Set the uids that cannot be faded
*
* <p>This is a negative list. Players with matching uid of this list will not be faded.
- * Uids that are not part of this list shall be considered for fading
- *
- * <p>Passing an empty list as input clears the existing list. This can be used to
- * reset the list when using a copy constructor
+ * Uids that are not part of this list shall be considered for fading.
*
* @param uids list of uids
* @return the same Builder instance
- * @throws NullPointerException if the uid list is {@code null}
* @see #getUnfadeableUids()
*/
@NonNull
@@ -1248,19 +1250,17 @@
}
/**
- * Remove the uid from unfadeable list
+ * Clears the unfadeable uid list
*
- * @param uid client uid
+ * <p>This can be used to reset the list when using a copy constructor.
+ *
* @return the same Builder instance
* @see #setUnfadeableUids(List)
* @see #getUnfadeableUids()
*/
@NonNull
- public Builder clearUnfadeableUid(int uid) {
- int index = mUnfadeableUids.indexOf(uid);
- if (index != INVALID_INDEX) {
- mUnfadeableUids.remove(index);
- }
+ public Builder clearUnfadeableUids() {
+ mUnfadeableUids.clear();
return this;
}
@@ -1270,24 +1270,19 @@
* <p>This is a negative list. Players with matching audio attributes of this list will not
* be faded. Audio attributes that are not part of this list shall be considered for fading.
*
- * <p>Passing an empty list as input clears any existing list. This can be used to
- * reset the list when using a copy constructor
- *
* <p><b>Note:</b> Be cautious when adding generic audio attributes into this list as it can
- * negatively impact fadeability decision if such an audio attribute and corresponding
- * usage fall into opposing lists.
+ * negatively impact fadeability decision (if such an audio attribute and corresponding
+ * usage fall into opposing lists).
* For example:
* <pre class=prettyprint>
* AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build() </pre>
* is a generic audio attribute for {@link android.media.AudioAttributes.USAGE_MEDIA}.
- * It is an undefined behavior to have an
- * {@link android.media.AudioAttributes.AttributeUsage} in the fadeable usage list and the
- * corresponding generic {@link android.media.AudioAttributes} in the unfadeable list. Such
- * cases will result in an exception during {@link #build()}
+ * It is an undefined behavior to have an {@link android.media.AudioAttributes usage} in the
+ * fadeable usage list and the corresponding generic {@link android.media.AudioAttributes}
+ * in the unfadeable list. Such cases will result in an exception during {@link #build()}.
*
* @param attrs list of {@link android.media.AudioAttributes}
* @return the same Builder instance
- * @throws NullPointerException if the audio attributes list is {@code null}
* @see #getUnfadeableAudioAttributes()
*/
@NonNull
@@ -1303,7 +1298,6 @@
*
* @param audioAttributes the {@link android.media.AudioAttributes}
* @return the same Builder instance
- * @throws NullPointerException if the audio attributes is {@code null}
* @see #setUnfadeableAudioAttributes(List)
* @see #getUnfadeableAudioAttributes()
*/
@@ -1317,19 +1311,16 @@
}
/**
- * Remove the {@link android.media.AudioAttributes} from the unfadeable list.
+ * Clears the unfadeable {@link android.media.AudioAttributes} list.
*
- * @param audioAttributes the {@link android.media.AudioAttributes}
+ * <p>This can be used to reset the list when using a copy constructor.
+ *
* @return the same Builder instance
- * @throws NullPointerException if the audio attributes is {@code null}
* @see #getUnfadeableAudioAttributes()
*/
@NonNull
- public Builder clearUnfadeableAudioAttributes(@NonNull AudioAttributes audioAttributes) {
- Objects.requireNonNull(audioAttributes, "Audio attributes cannot be null");
- if (mUnfadeableAudioAttributes.contains(audioAttributes)) {
- mUnfadeableAudioAttributes.remove(audioAttributes);
- }
+ public Builder clearUnfadeableAudioAttributes() {
+ mUnfadeableAudioAttributes.clear();
return this;
}
@@ -1345,7 +1336,7 @@
* @see #getFadeInDelayForOffenders()
*/
@NonNull
- public Builder setFadeInDelayForOffenders(long delayMillis) {
+ public Builder setFadeInDelayForOffenders(@DurationMillisLong long delayMillis) {
Preconditions.checkArgument(delayMillis >= 0, "Delay cannot be negative");
mFadeInDelayForOffendersMillis = delayMillis;
return this;
@@ -1469,7 +1460,6 @@
switch(state) {
case FADE_STATE_DISABLED:
case FADE_STATE_ENABLED_DEFAULT:
- case FADE_STATE_ENABLED_AUTO:
break;
default:
throw new IllegalArgumentException("Unknown fade state: " + state);
diff --git a/media/java/android/media/browse/MediaBrowserUtils.java b/media/java/android/media/browse/MediaBrowserUtils.java
index 19d9f00..8c008bc 100644
--- a/media/java/android/media/browse/MediaBrowserUtils.java
+++ b/media/java/android/media/browse/MediaBrowserUtils.java
@@ -18,6 +18,9 @@
import android.os.Bundle;
+import java.util.Collections;
+import java.util.List;
+
/**
* @hide
*/
@@ -75,4 +78,29 @@
}
return false;
}
+
+ /**
+ * Returns a paged version of the given {@code list}, using the paging parameters in {@code
+ * options}.
+ */
+ public static List<MediaBrowser.MediaItem> applyPagingOptions(
+ List<MediaBrowser.MediaItem> list, final Bundle options) {
+ if (list == null) {
+ return null;
+ }
+ int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
+ int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+ if (page == -1 && pageSize == -1) {
+ return list;
+ }
+ int fromIndex = pageSize * page;
+ int toIndex = fromIndex + pageSize;
+ if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
+ return Collections.EMPTY_LIST;
+ }
+ if (toIndex > list.size()) {
+ toIndex = list.size();
+ }
+ return list.subList(fromIndex, toIndex);
+ }
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index e8ef464..ba7ab9a 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -48,7 +48,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -728,7 +727,7 @@
List<MediaBrowser.MediaItem> filteredList =
(flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
- ? applyOptions(list, options) : list;
+ ? MediaBrowserUtils.applyPagingOptions(list, options) : list;
final ParceledListSlice<MediaBrowser.MediaItem> pls;
if (filteredList == null) {
pls = null;
@@ -762,27 +761,6 @@
}
}
- private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list,
- final Bundle options) {
- if (list == null) {
- return null;
- }
- int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
- int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
- if (page == -1 && pageSize == -1) {
- return list;
- }
- int fromIndex = pageSize * page;
- int toIndex = fromIndex + pageSize;
- if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
- return Collections.EMPTY_LIST;
- }
- if (toIndex > list.size()) {
- toIndex = list.size();
- }
- return list.subList(fromIndex, toIndex);
- }
-
private void performLoadItem(String itemId, final ConnectionRecord connection,
final ResultReceiver receiver) {
final Result<MediaBrowser.MediaItem> result =
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
index f105ae9..236b1fd 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
@@ -45,8 +45,10 @@
@RunWith(AndroidJUnit4.class)
@RequiresFlagsEnabled(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
public final class FadeManagerConfigurationUnitTest {
- private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
- private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000;
+ private static final long DEFAULT_FADE_OUT_DURATION_MS =
+ FadeManagerConfiguration.getDefaultFadeOutDurationMillis();
+ private static final long DEFAULT_FADE_IN_DURATION_MS =
+ FadeManagerConfiguration.getDefaultFadeInDurationMillis();
private static final long TEST_FADE_OUT_DURATION_MS = 1_500;
private static final long TEST_FADE_IN_DURATION_MS = 750;
private static final int TEST_INVALID_USAGE = -10;
@@ -259,16 +261,6 @@
}
@Test
- public void testSetFadeState_toEnableAuto() {
- final int fadeStateAuto = FadeManagerConfiguration.FADE_STATE_ENABLED_AUTO;
- FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder()
- .setFadeState(fadeStateAuto).build();
-
- expect.withMessage("Fade state when enabled for audio").that(fmc.getFadeState())
- .isEqualTo(fadeStateAuto);
- }
-
- @Test
public void testSetFadeState_toInvalid_fails() {
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
new FadeManagerConfiguration.Builder()
@@ -310,13 +302,13 @@
}
@Test
- public void testSetFadeVolShaperConfig_withNullVolumeShaper_getsNull() {
+ public void testSetFadeOutVolShaperConfig_withNullVolumeShaper_getsNull() {
FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder(mFmc)
.setFadeOutVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE,
/* VolumeShaper.Configuration= */ null)
.setFadeInVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE,
/* VolumeShaper.Configuration= */ null)
- .clearFadeableUsage(AudioAttributes.USAGE_MEDIA).build();
+ .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_MEDIA).build();
expect.withMessage("Fade out volume shaper config set with null value")
.that(fmc.getFadeOutVolumeShaperConfigForAudioAttributes(
@@ -547,31 +539,13 @@
}
@Test
- public void testClearFadeableUsage() {
- final int usageToClear = AudioAttributes.USAGE_MEDIA;
- List<Integer> updatedUsages = new ArrayList<>(mFmc.getFadeableUsages());
- updatedUsages.remove((Integer) usageToClear);
+ public void testClearFadeableUsages() {
+ FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(mFmc)
+ .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ .build();
- FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration
- .Builder(mFmc).clearFadeableUsage(usageToClear).build();
-
- expect.withMessage("Clear fadeable usage").that(updatedFmc.getFadeableUsages())
- .containsExactlyElementsIn(updatedUsages);
- }
-
- @Test
- public void testClearFadeableUsage_withInvalidUsage_fails() {
- FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc);
-
- IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
- fmcBuilder.clearFadeableUsage(TEST_INVALID_USAGE)
- );
-
- FadeManagerConfiguration fmc = fmcBuilder.build();
- expect.withMessage("Clear invalid usage").that(thrown).hasMessageThat()
- .contains("Invalid usage");
- expect.withMessage("Fadeable usages").that(fmc.getFadeableUsages())
- .containsExactlyElementsIn(mFmc.getFadeableUsages());
+ expect.withMessage("Clear fadeable usages").that(updatedFmc.getFadeableUsages())
+ .containsExactlyElementsIn(List.of(AudioAttributes.USAGE_VOICE_COMMUNICATION));
}
@Test
@@ -673,7 +647,7 @@
}
@Test
- public void testClearUnfadeableContentType() {
+ public void testClearUnfadeableContentTypes() {
List<Integer> unfadeableContentTypes = new ArrayList<>(Arrays.asList(
AudioAttributes.CONTENT_TYPE_MOVIE,
AudioAttributes.CONTENT_TYPE_SONIFICATION
@@ -682,23 +656,10 @@
FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder()
.setUnfadeableContentTypes(unfadeableContentTypes)
- .clearUnfadeableContentType(contentTypeToClear).build();
+ .clearUnfadeableContentTypes().build();
- unfadeableContentTypes.remove((Integer) contentTypeToClear);
expect.withMessage("Unfadeable content types").that(updatedFmc.getUnfadeableContentTypes())
- .containsExactlyElementsIn(unfadeableContentTypes);
- }
-
- @Test
- public void testClearUnfadeableContentType_withInvalidContentType_fails() {
- FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc);
-
- IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
- fmcBuilder.clearUnfadeableContentType(TEST_INVALID_CONTENT_TYPE).build()
- );
-
- expect.withMessage("Invalid content type exception").that(thrown).hasMessageThat()
- .contains("Invalid content type");
+ .isEmpty();
}
@Test
@@ -735,7 +696,7 @@
}
@Test
- public void testClearUnfadebaleUid() {
+ public void testClearUnfadebaleUids() {
final List<Integer> unfadeableUids = List.of(
TEST_UID_1,
TEST_UID_2
@@ -744,10 +705,9 @@
.setUnfadeableUids(unfadeableUids).build();
FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(fmc)
- .clearUnfadeableUid(TEST_UID_1).build();
+ .clearUnfadeableUids().build();
- expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids())
- .isEqualTo(List.of(TEST_UID_2));
+ expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids()).isEmpty();
}
@Test
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 9a2cf61..e7d1072 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -40,7 +40,7 @@
Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this")
}
if (showCancel) {
- val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+ val appLabel = packageManager.appLabel(cancelUiRequest.packageName)
if (appLabel == null) {
Log.d(TAG, "Received UI cancel request with an invalid package name.")
null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 0ccb07a..3097387 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -58,6 +58,7 @@
private val providerEnabledList: List<ProviderData>
private val providerDisabledList: List<DisabledProviderData>?
val resultReceiver: ResultReceiver?
+ val finalResponseReceiver: ResultReceiver?
var initialUiState: UiState
@@ -105,6 +106,11 @@
ResultReceiver::class.java
)
+ finalResponseReceiver = intent.getParcelableExtra(
+ Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver::class.java
+ )
+
isReqForAllOptions = intent.getBooleanExtra(
Constants.EXTRA_REQ_FOR_ALL_OPTIONS,
/*defaultValue=*/ false
@@ -113,7 +119,7 @@
val cancellationRequest = getCancelUiRequest(intent)
val cancelUiRequestState = cancellationRequest?.let {
- CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
+ CancelUiRequestState(getAppLabel(context.getPackageManager(), it.packageName))
}
initialUiState = when (requestInfo?.type) {
@@ -200,7 +206,7 @@
}
fun onCancel(cancelCode: Int) {
- sendCancellationCode(cancelCode, requestInfo?.token, resultReceiver)
+ sendCancellationCode(cancelCode, requestInfo?.token, resultReceiver, finalResponseReceiver)
}
fun onOptionSelected(
@@ -219,6 +225,10 @@
)
val resultDataBundle = Bundle()
UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+
+ resultDataBundle.putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ finalResponseReceiver)
+
resultReceiver?.send(
BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
resultDataBundle
@@ -286,10 +296,14 @@
fun sendCancellationCode(
cancelCode: Int,
requestToken: IBinder?,
- resultReceiver: ResultReceiver?
+ resultReceiver: ResultReceiver?,
+ finalResponseReceiver: ResultReceiver?
) {
if (requestToken != null && resultReceiver != null) {
val resultData = Bundle()
+ resultData.putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ finalResponseReceiver)
+
BaseDialogResult.addToBundle(BaseDialogResult(requestToken), resultData)
resultReceiver.send(cancelCode, resultData)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 05aa548..4771237 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -135,7 +135,7 @@
Log.d(
Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" +
" ui = $shouldShowCancellationUi")
- val appDisplayName = getAppLabel(packageManager, cancelUiRequest.appPackageName)
+ val appDisplayName = getAppLabel(packageManager, cancelUiRequest.packageName)
if (!shouldShowCancellationUi) {
this.finish()
}
@@ -216,13 +216,18 @@
android.credentials.selection.Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
)
+ val finalResponseResultReceiver = intent.getParcelableExtra(
+ android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver::class.java
+ )
+
val requestInfo = intent.extras?.getParcelable(
RequestInfo.EXTRA_REQUEST_INFO,
RequestInfo::class.java
)
CredentialManagerRepo.sendCancellationCode(
BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE,
- requestInfo?.token, resultReceiver
+ requestInfo?.token, resultReceiver, finalResponseResultReceiver
)
this.finish()
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 6c5a984..f4da1e6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -72,7 +72,7 @@
init {
uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INIT,
- credManRepo.requestInfo?.appPackageName)
+ credManRepo.requestInfo?.packageName)
}
/**************************************************************************/
@@ -107,7 +107,7 @@
if (this.credManRepo.requestInfo?.token != credManRepo.requestInfo?.token) {
this.uiMetrics.resetInstanceId()
this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_NEW_REQUEST,
- credManRepo.requestInfo?.appPackageName)
+ credManRepo.requestInfo?.packageName)
}
}
@@ -189,7 +189,7 @@
private fun onInternalError() {
Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INTERNAL_ERROR,
- credManRepo.requestInfo?.appPackageName)
+ credManRepo.requestInfo?.packageName)
credManRepo.onParsingFailureCancel()
uiState = uiState.copy(dialogState = DialogState.COMPLETE)
}
@@ -399,6 +399,6 @@
@Composable
fun logUiEvent(uiEventEnum: UiEventEnum) {
- this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.appPackageName)
+ this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName)
}
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 64595e2..997c45e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -195,7 +195,7 @@
}
return com.android.credentialmanager.getflow.RequestDisplayInfo(
appName = originName?.ifEmpty { null }
- ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
+ ?: getAppLabel(context.packageManager, requestInfo.packageName)
?: return null,
preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
preferIdentityDocUi = getCredentialRequest.data.getBoolean(
@@ -269,7 +269,7 @@
return null
}
val appLabel = originName?.ifEmpty { null }
- ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
+ ?: getAppLabel(context.packageManager, requestInfo.packageName)
?: return null
val createCredentialRequest = requestInfo.createCredentialRequest ?: return null
val createCredentialRequestJetpack = CreateCredentialRequest.createFrom(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 2628f09..8fde5d7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -19,13 +19,12 @@
import android.app.PendingIntent
import android.app.assist.AssistStructure
import android.content.Context
-import android.credentials.Credential
+import android.content.Intent
import android.credentials.CredentialManager
-import android.credentials.CredentialOption
-import android.credentials.GetCandidateCredentialsException
-import android.credentials.GetCandidateCredentialsResponse
import android.credentials.GetCredentialRequest
-import android.credentials.GetCredentialResponse
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.CredentialOption
import android.credentials.selection.Entry
import android.credentials.selection.GetCredentialProviderData
import android.credentials.selection.ProviderData
@@ -47,7 +46,6 @@
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
-import android.view.autofill.AutofillValue
import android.view.autofill.IAutoFillManagerClient
import android.widget.RemoteViews
import android.widget.inline.InlinePresentationSpec
@@ -131,30 +129,7 @@
val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
GetCandidateCredentialsException> {
override fun onResult(result: GetCandidateCredentialsResponse) {
- Log.i(TAG, "getCandidateCredentials onResponse")
-
- if (result.getCredentialResponse != null) {
- val autofillId: AutofillId? = result.getCredentialResponse
- .credential.data.getParcelable(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- AutofillId::class.java)
- Log.i(TAG, "getCandidateCredentials final response, autofillId: " +
- autofillId)
-
- if (autofillId != null) {
- autofillCallback.autofill(
- sessionId,
- mutableListOf(autofillId),
- mutableListOf(
- AutofillValue.forText(
- convertResponseToJson(result.getCredentialResponse)
- )
- ),
- false)
- }
- return
- }
-
+ Log.i(TAG, "getCandidateCredentials onResult")
val fillResponse = convertToFillResponse(result, request,
responseClientState)
if (fillResponse != null) {
@@ -181,57 +156,6 @@
)
}
- // TODO(b/318118018): Use from Jetpack
- private fun convertResponseToJson(response: GetCredentialResponse): String? {
- try {
- val jsonObject = JSONObject()
- jsonObject.put("type", "get")
- val jsonCred = JSONObject()
- jsonCred.put("type", response.credential.type)
- jsonCred.put("data", credentialToJSON(
- response.credential))
- jsonObject.put("credential", jsonCred)
- return jsonObject.toString()
- } catch (e: JSONException) {
- Log.i(
- TAG, "Exception while constructing response JSON: " +
- e.message
- )
- }
- return null
- }
-
- // TODO(b/318118018): Replace with calls to Jetpack
- private fun credentialToJSON(credential: Credential): JSONObject? {
- Log.i(TAG, "credentialToJSON")
- try {
- if (credential.type == "android.credentials.TYPE_PASSWORD_CREDENTIAL") {
- Log.i(TAG, "toJSON PasswordCredential")
-
- val json = JSONObject()
- val id = credential.data.getString("androidx.credentials.BUNDLE_KEY_ID")
- val pass = credential.data.getString("androidx.credentials.BUNDLE_KEY_PASSWORD")
- json.put("androidx.credentials.BUNDLE_KEY_ID", id)
- json.put("androidx.credentials.BUNDLE_KEY_PASSWORD", pass)
- return json
- } else if (credential.type == "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL") {
- Log.i(TAG, "toJSON PublicKeyCredential")
-
- val json = JSONObject()
- val responseJson = credential
- .data
- .getString("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON")
- json.put("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON",
- responseJson)
- return json
- }
- } catch (e: JSONException) {
- Log.i(TAG, "issue while converting credential response to JSON")
- }
- Log.i(TAG, "Unsupported credential type")
- return null
- }
-
private fun getEntryToIconMap(
candidateProviderDataList: List<GetCredentialProviderData>
): Map<String, Icon> {
@@ -275,6 +199,7 @@
val autofillIdToProvidersMap: Map<AutofillId, ArrayList<GetCredentialProviderData>> =
mapAutofillIdToProviders(candidateProviders)
val fillResponseBuilder = FillResponse.Builder()
+ fillResponseBuilder.setFlags(FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE)
var validFillResponse = false
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
validFillResponse = processProvidersForAutofillId(
@@ -387,7 +312,7 @@
presentationBuilder.build())
.build())
.setAuthentication(pendingIntent.intentSender)
- .setAuthenticationExtras(fillInIntent.extras)
+ .setCredentialFillInIntent(fillInIntent)
.build())
datasetAdded = true
i++
@@ -407,11 +332,11 @@
}
private fun createInlinePresentation(
- primaryEntry: CredentialEntryInfo,
- pendingIntent: PendingIntent,
- icon: Icon,
- spec: InlinePresentationSpec,
- duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>
+ primaryEntry: CredentialEntryInfo,
+ pendingIntent: PendingIntent,
+ icon: Icon,
+ spec: InlinePresentationSpec,
+ duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>
): InlinePresentation {
val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY &&
primaryEntry.displayName != null) {
@@ -437,7 +362,8 @@
fillResponseBuilder: FillResponse.Builder
) {
val presentationBuilder = Presentations.Builder()
- .setMenuPresentation(RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
+ .setMenuPresentation(
+ RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
fillResponseBuilder.addDataset(
Dataset.Builder()
@@ -477,9 +403,8 @@
.setInlinePresentation(InlinePresentation(
sliceBuilder.build().slice, spec, /* pinned= */ true))
- val extraBundle = Bundle()
- extraBundle.putParcelableArrayList(
- ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
+ val extrasIntent = Intent()
+ extrasIntent.putExtra(ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
fillResponseBuilder.addDataset(
dataSetBuilder
@@ -489,7 +414,7 @@
presentationBuilder.build())
.build())
.setAuthentication(bottomSheetPendingIntent.intentSender)
- .setAuthenticationExtras(extraBundle)
+ .setCredentialFillInIntent(extrasIntent)
.build()
)
}
@@ -640,7 +565,6 @@
autofillId: AutofillId,
responseClientState: Bundle
): List<CredentialOption> {
- // TODO(b/293945193) Replace with isCredential check from viewNode
val credentialHints: MutableList<String> = mutableListOf()
if (viewNode.autofillHints != null) {
for (hint in viewNode.autofillHints!!) {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index 3297991..5590219 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -76,7 +76,7 @@
Chip(
label = labelParam,
onClick = onClick,
- modifier = modifier,
+ modifier = modifier.fillMaxWidth(),
secondaryLabel = secondaryLabelParam,
icon = iconParam,
colors = colors,
@@ -104,7 +104,6 @@
label = stringResource(R.string.dialog_sign_in_options_button),
onClick = onClick,
modifier = Modifier
- .fillMaxWidth()
.padding(top = TOPPADDING)
)
}
@@ -121,7 +120,6 @@
label = stringResource(R.string.dialog_continue_button),
onClick = onClick,
modifier = Modifier
- .fillMaxWidth()
.padding(top = TOPPADDING),
colors = ChipDefaults.primaryChipColors(),
)
@@ -139,7 +137,6 @@
label = stringResource(R.string.dialog_dismiss_button),
onClick = onClick,
modifier = Modifier
- .fillMaxWidth()
.padding(top = TOPPADDING),
)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
new file mode 100644
index 0000000..2a4658b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.settingslib.media.data.repository
+
+import android.media.AudioDeviceAttributes
+import android.media.Spatializer
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+interface SpatializerRepository {
+
+ /**
+ * Returns true when Spatial audio feature is supported for the [audioDeviceAttributes] and
+ * false the otherwise.
+ */
+ suspend fun isAvailableForDevice(audioDeviceAttributes: AudioDeviceAttributes): Boolean
+
+ /** Returns a list [AudioDeviceAttributes] that are compatible with spatial audio. */
+ suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes>
+
+ /** Adds a [audioDeviceAttributes] to [getCompatibleDevices] list. */
+ suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes)
+
+ /** Removes a [audioDeviceAttributes] to [getCompatibleDevices] list. */
+ suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes)
+}
+
+class SpatializerRepositoryImpl(
+ private val spatializer: Spatializer,
+ private val backgroundContext: CoroutineContext,
+) : SpatializerRepository {
+
+ override suspend fun isAvailableForDevice(
+ audioDeviceAttributes: AudioDeviceAttributes
+ ): Boolean {
+ return withContext(backgroundContext) {
+ spatializer.isAvailableForDevice(audioDeviceAttributes)
+ }
+ }
+
+ override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> =
+ withContext(backgroundContext) { spatializer.compatibleAudioDevices }
+
+ override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+ withContext(backgroundContext) {
+ spatializer.addCompatibleAudioDevice(audioDeviceAttributes)
+ }
+ }
+
+ override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+ withContext(backgroundContext) {
+ spatializer.removeCompatibleAudioDevice(audioDeviceAttributes)
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
new file mode 100644
index 0000000..c3cc340
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import com.android.settingslib.media.data.repository.SpatializerRepository
+
+class SpatializerInteractor(private val repository: SpatializerRepository) {
+
+ suspend fun isAvailable(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
+ repository.isAvailableForDevice(audioDeviceAttributes)
+
+ /** Checks if spatial audio is enabled for the [audioDeviceAttributes]. */
+ suspend fun isEnabled(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
+ repository.getCompatibleDevices().contains(audioDeviceAttributes)
+
+ /** Enblaes or disables spatial audio for [audioDeviceAttributes]. */
+ suspend fun setEnabled(audioDeviceAttributes: AudioDeviceAttributes, isEnabled: Boolean) {
+ if (isEnabled) {
+ repository.addCompatibleDevice(audioDeviceAttributes)
+ } else {
+ repository.removeCompatibleDevice(audioDeviceAttributes)
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/model/RoutingSession.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/RoutingSession.kt
new file mode 100644
index 0000000..a98f3e2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/RoutingSession.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.settingslib.volume.data.model
+
+import android.media.RoutingSessionInfo
+
+/** Models a routing session which is created when a media route is selected. */
+data class RoutingSession(
+ val routingSessionInfo: RoutingSessionInfo,
+ val isVolumeSeekBarEnabled: Boolean,
+ val isMediaOutputDisabled: Boolean,
+)
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 6761aa7..f729c04 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -16,15 +16,12 @@
package com.android.settingslib.volume.data.repository
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -32,7 +29,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
@@ -40,7 +36,6 @@
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -77,7 +72,7 @@
}
class AudioRepositoryImpl(
- private val context: Context,
+ private val audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
private val audioManager: AudioManager,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
@@ -93,30 +88,9 @@
.flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
- private val audioManagerIntents: SharedFlow<String> =
- callbackFlow {
- val receiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent) {
- intent.action?.let { action -> launch { send(action) } }
- }
- }
- context.registerReceiver(
- receiver,
- IntentFilter().apply {
- for (action in allActions) {
- addAction(action)
- }
- }
- )
-
- awaitClose { context.unregisterReceiver(receiver) }
- }
- .shareIn(coroutineScope, SharingStarted.WhileSubscribed())
-
override val ringerMode: StateFlow<RingerMode> =
- audioManagerIntents
- .filter { ringerActions.contains(it) }
+ audioManagerIntentsReceiver.intents
+ .filter { AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION == it.action }
.map { RingerMode(audioManager.ringerModeInternal) }
.flowOn(backgroundCoroutineContext)
.stateIn(
@@ -146,8 +120,7 @@
)
override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
- return audioManagerIntents
- .filter { modelActions.contains(it) }
+ return audioManagerIntentsReceiver.intents
.map { getCurrentAudioStream(audioStream) }
.flowOn(backgroundCoroutineContext)
}
@@ -189,20 +162,4 @@
// return STREAM_VOICE_CALL in getAudioStream
audioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL)
}
-
- private companion object {
- val modelActions =
- setOf(
- AudioManager.STREAM_MUTE_CHANGED_ACTION,
- AudioManager.MASTER_MUTE_CHANGED_ACTION,
- AudioManager.VOLUME_CHANGED_ACTION,
- AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
- AudioManager.STREAM_DEVICES_CHANGED_ACTION,
- )
- val ringerActions =
- setOf(
- AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
- )
- val allActions = ringerActions + modelActions
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index 1597b77..aa9ae76 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -15,8 +15,13 @@
*/
package com.android.settingslib.volume.data.repository
+import android.media.AudioManager
+import android.media.MediaRouter2Manager
+import android.media.RoutingSessionInfo
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.volume.data.model.RoutingSession
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -24,10 +29,15 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Repository providing data about connected media devices. */
interface LocalMediaRepository {
@@ -37,43 +47,55 @@
/** Currently connected media device */
val currentConnectedDevice: StateFlow<MediaDevice?>
+
+ val remoteRoutingSessions: StateFlow<Collection<RoutingSession>>
+
+ suspend fun adjustSessionVolume(sessionId: String?, volume: Int)
}
class LocalMediaRepositoryImpl(
+ audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
private val localMediaManager: LocalMediaManager,
+ private val mediaRouter2Manager: MediaRouter2Manager,
coroutineScope: CoroutineScope,
- backgroundContext: CoroutineContext,
+ private val backgroundContext: CoroutineContext,
) : LocalMediaRepository {
- private val deviceUpdates: Flow<DevicesUpdate> = callbackFlow {
- val callback =
- object : LocalMediaManager.DeviceCallback {
- override fun onDeviceListUpdate(newDevices: List<MediaDevice>?) {
- trySend(DevicesUpdate.DeviceListUpdate(newDevices ?: emptyList()))
- }
+ private val devicesChanges =
+ audioManagerIntentsReceiver.intents.filter {
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
+ }
+ private val mediaDevicesUpdates: Flow<DevicesUpdate> =
+ callbackFlow {
+ val callback =
+ object : LocalMediaManager.DeviceCallback {
+ override fun onDeviceListUpdate(newDevices: List<MediaDevice>?) {
+ trySend(DevicesUpdate.DeviceListUpdate(newDevices ?: emptyList()))
+ }
- override fun onSelectedDeviceStateChanged(
- device: MediaDevice?,
- state: Int,
- ) {
- trySend(DevicesUpdate.SelectedDeviceStateChanged)
- }
+ override fun onSelectedDeviceStateChanged(
+ device: MediaDevice?,
+ state: Int,
+ ) {
+ trySend(DevicesUpdate.SelectedDeviceStateChanged)
+ }
- override fun onDeviceAttributesChanged() {
- trySend(DevicesUpdate.DeviceAttributesChanged)
+ override fun onDeviceAttributesChanged() {
+ trySend(DevicesUpdate.DeviceAttributesChanged)
+ }
+ }
+ localMediaManager.registerCallback(callback)
+ localMediaManager.startScan()
+
+ awaitClose {
+ localMediaManager.stopScan()
+ localMediaManager.unregisterCallback(callback)
}
}
- localMediaManager.registerCallback(callback)
- localMediaManager.startScan()
-
- awaitClose {
- localMediaManager.stopScan()
- localMediaManager.unregisterCallback(callback)
- }
- }
+ .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
override val mediaDevices: StateFlow<Collection<MediaDevice>> =
- deviceUpdates
+ mediaDevicesUpdates
.mapNotNull {
if (it is DevicesUpdate.DeviceListUpdate) {
it.newDevices ?: emptyList()
@@ -85,7 +107,7 @@
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
override val currentConnectedDevice: StateFlow<MediaDevice?> =
- deviceUpdates
+ merge(devicesChanges, mediaDevicesUpdates)
.map { localMediaManager.currentConnectedDevice }
.stateIn(
coroutineScope,
@@ -93,6 +115,30 @@
localMediaManager.currentConnectedDevice
)
+ override val remoteRoutingSessions: StateFlow<Collection<RoutingSession>> =
+ merge(devicesChanges, mediaDevicesUpdates)
+ .onStart { emit(Unit) }
+ .map { localMediaManager.remoteRoutingSessions.map(::toRoutingSession) }
+ .flowOn(backgroundContext)
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
+
+ override suspend fun adjustSessionVolume(sessionId: String?, volume: Int) {
+ withContext(backgroundContext) {
+ if (sessionId == null) {
+ localMediaManager.adjustSessionVolume(volume)
+ } else {
+ localMediaManager.adjustSessionVolume(sessionId, volume)
+ }
+ }
+ }
+
+ private fun toRoutingSession(info: RoutingSessionInfo): RoutingSession =
+ RoutingSession(
+ info,
+ isMediaOutputDisabled = mediaRouter2Manager.getTransferableRoutes(info).isEmpty(),
+ isVolumeSeekBarEnabled = localMediaManager.shouldEnableVolumeSeekBar(info)
+ )
+
private sealed interface DevicesUpdate {
data class DeviceListUpdate(val newDevices: List<MediaDevice>?) : DevicesUpdate
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
index 93aa90d..ab8c6b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
@@ -16,30 +16,23 @@
package com.android.settingslib.volume.data.repository
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.headsetAudioModeChanges
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Provides controllers for currently active device media sessions. */
interface MediaControllerRepository {
@@ -49,40 +42,25 @@
}
class MediaControllerRepositoryImpl(
- private val context: Context,
+ audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
private val mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
coroutineScope: CoroutineScope,
backgroundContext: CoroutineContext,
) : MediaControllerRepository {
- private val devicesChanges: Flow<Unit> =
- callbackFlow {
- val receiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- if (AudioManager.STREAM_DEVICES_CHANGED_ACTION == intent?.action) {
- launch { send(Unit) }
- }
- }
- }
- context.registerReceiver(
- receiver,
- IntentFilter(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
- )
-
- awaitClose { context.unregisterReceiver(receiver) }
- }
- .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
-
+ private val devicesChanges =
+ audioManagerIntentsReceiver.intents.filter {
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
+ }
override val activeMediaController: StateFlow<MediaController?> =
- combine(
- localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) }
- ?: emptyFlow(),
- devicesChanges.onStart { emit(Unit) },
- ) { _, _ ->
- getActiveLocalMediaController()
+ buildList {
+ localBluetoothManager?.headsetAudioModeChanges?.let { add(it) }
+ add(devicesChanges)
}
+ .merge()
+ .onStart { emit(Unit) }
+ .map { getActiveLocalMediaController() }
.flowOn(backgroundContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/LocalMediaInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/LocalMediaInteractor.kt
new file mode 100644
index 0000000..f621335
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/LocalMediaInteractor.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.settingslib.volume.domain.interactor
+
+import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
+import com.android.settingslib.volume.domain.model.RoutingSession
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+class LocalMediaInteractor(
+ private val repository: LocalMediaRepository,
+ coroutineScope: CoroutineScope,
+) {
+
+ /** Available devices list */
+ val mediaDevices: StateFlow<Collection<MediaDevice>>
+ get() = repository.mediaDevices
+
+ /** Currently connected media device */
+ val currentConnectedDevice: StateFlow<MediaDevice?>
+ get() = repository.currentConnectedDevice
+
+ val remoteRoutingSessions: StateFlow<List<RoutingSession>> =
+ repository.remoteRoutingSessions
+ .map { sessions ->
+ sessions.map {
+ RoutingSession(
+ routingSessionInfo = it.routingSessionInfo,
+ isMediaOutputDisabled = it.isMediaOutputDisabled,
+ isVolumeSeekBarEnabled =
+ it.isVolumeSeekBarEnabled && it.routingSessionInfo.volumeMax > 0
+ )
+ }
+ }
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
+
+ suspend fun adjustSessionVolume(sessionId: String?, volume: Int) =
+ repository.adjustSessionVolume(sessionId, volume)
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/model/RoutingSession.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/model/RoutingSession.kt
new file mode 100644
index 0000000..dfc4703
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/model/RoutingSession.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.settingslib.volume.domain.model
+
+import android.media.RoutingSessionInfo
+
+/** Models a routing session which is created when a media route is selected. */
+data class RoutingSession(
+ val routingSessionInfo: RoutingSessionInfo,
+ val isMediaOutputDisabled: Boolean,
+ val isVolumeSeekBarEnabled: Boolean,
+)
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt
new file mode 100644
index 0000000..9fa4c86
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.settingslib.volume.shared
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.media.AudioManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+
+/** Exposes [AudioManager] intents as a observable shared flow. */
+interface AudioManagerIntentsReceiver {
+
+ val intents: SharedFlow<Intent>
+}
+
+class AudioManagerIntentsReceiverImpl(
+ private val context: Context,
+ coroutineScope: CoroutineScope,
+) : AudioManagerIntentsReceiver {
+
+ private val allActions: Collection<String>
+ get() =
+ setOf(
+ AudioManager.STREAM_MUTE_CHANGED_ACTION,
+ AudioManager.MASTER_MUTE_CHANGED_ACTION,
+ AudioManager.VOLUME_CHANGED_ACTION,
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION,
+ )
+
+ override val intents: SharedFlow<Intent> =
+ callbackFlow {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ launch { send(intent) }
+ }
+ }
+ context.registerReceiver(
+ receiver,
+ IntentFilter().apply {
+ for (action in allActions) {
+ addAction(action)
+ }
+ }
+ )
+
+ awaitClose { context.unregisterReceiver(receiver) }
+ }
+ .filterNotNull()
+ .filter { intent -> allActions.contains(intent.action) }
+ .shareIn(coroutineScope, SharingStarted.WhileSubscribed())
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt
new file mode 100644
index 0000000..3f52f24
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import com.android.settingslib.media.data.repository.SpatializerRepository
+
+class FakeSpatializerRepository : SpatializerRepository {
+
+ private val availabilityByDevice: MutableMap<AudioDeviceAttributes, Boolean> = mutableMapOf()
+ private val compatibleDevices: MutableList<AudioDeviceAttributes> = mutableListOf()
+
+ override suspend fun isAvailableForDevice(
+ audioDeviceAttributes: AudioDeviceAttributes
+ ): Boolean = availabilityByDevice.getOrDefault(audioDeviceAttributes, false)
+
+ override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> =
+ compatibleDevices
+
+ override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+ compatibleDevices.add(audioDeviceAttributes)
+ }
+
+ override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+ compatibleDevices.remove(audioDeviceAttributes)
+ }
+
+ fun setIsAvailable(audioDeviceAttributes: AudioDeviceAttributes, isAvailable: Boolean) {
+ availabilityByDevice[audioDeviceAttributes] = isAvailable
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt
new file mode 100644
index 0000000..a44baeb
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SpatializerInteractorTest {
+
+ private val testScope = TestScope()
+ private val underTest = SpatializerInteractor(FakeSpatializerRepository())
+
+ @Test
+ fun setEnabledFalse_isEnabled_false() {
+ testScope.runTest {
+ underTest.setEnabled(deviceAttributes, false)
+
+ assertThat(underTest.isEnabled(deviceAttributes)).isFalse()
+ }
+ }
+
+ @Test
+ fun setEnabledTrue_isEnabled_true() {
+ testScope.runTest {
+ underTest.setEnabled(deviceAttributes, true)
+
+ assertThat(underTest.isEnabled(deviceAttributes)).isTrue()
+ }
+ }
+
+ private companion object {
+ val deviceAttributes = AudioDeviceAttributes(0, 0, "test_device")
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 7b70c64..48b04db 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -16,13 +16,11 @@
package com.android.settingslib.volume.data.repository
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -30,6 +28,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -51,17 +50,16 @@
@RunWith(AndroidJUnit4::class)
class AudioRepositoryTest {
- @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
@Captor
private lateinit var modeListenerCaptor: ArgumentCaptor<AudioManager.OnModeChangedListener>
@Captor
private lateinit var communicationDeviceListenerCaptor:
ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener>
- @Mock private lateinit var context: Context
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
+ private val intentsReceiver = FakeAudioManagerIntentsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val isMuteByStream: MutableMap<Int, Boolean> = mutableMapOf()
@@ -98,7 +96,7 @@
underTest =
AudioRepositoryImpl(
- context,
+ intentsReceiver,
audioManager,
testScope.testScheduler,
testScope.backgroundScope,
@@ -270,8 +268,7 @@
}
private fun triggerIntent(action: String) {
- verify(context).registerReceiver(receiverCaptor.capture(), any())
- receiverCaptor.value.onReceive(context, Intent(action))
+ testScope.launch { intentsReceiver.triggerIntent(action) }
}
private companion object {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt
new file mode 100644
index 0000000..642b72c
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.settingslib.volume.data.repository
+
+import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.volume.data.model.RoutingSession
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeLocalMediaRepository : LocalMediaRepository {
+
+ private val volumeBySession: MutableMap<String?, Int> = mutableMapOf()
+
+ private val mutableMediaDevices = MutableStateFlow<Collection<MediaDevice>>(emptyList())
+ override val mediaDevices: StateFlow<Collection<MediaDevice>>
+ get() = mutableMediaDevices.asStateFlow()
+
+ private val mutableCurrentConnectedDevice = MutableStateFlow<MediaDevice?>(null)
+ override val currentConnectedDevice: StateFlow<MediaDevice?>
+ get() = mutableCurrentConnectedDevice.asStateFlow()
+
+ private val mutableRemoteRoutingSessions =
+ MutableStateFlow<Collection<RoutingSession>>(emptyList())
+ override val remoteRoutingSessions: StateFlow<Collection<RoutingSession>>
+ get() = mutableRemoteRoutingSessions.asStateFlow()
+
+ fun updateMediaDevices(devices: Collection<MediaDevice>) {
+ mutableMediaDevices.value = devices
+ }
+
+ fun updateCurrentConnectedDevice(device: MediaDevice?) {
+ mutableCurrentConnectedDevice.value = device
+ }
+
+ fun updateRemoteRoutingSessions(sessions: List<RoutingSession>) {
+ mutableRemoteRoutingSessions.value = sessions
+ }
+
+ fun getSessionVolume(sessionId: String?): Int = volumeBySession.getOrDefault(sessionId, 0)
+
+ override suspend fun adjustSessionVolume(sessionId: String?, volume: Int) {
+ volumeBySession[sessionId] = volume
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
index d106bce..dc9ea10 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
@@ -15,10 +15,15 @@
*/
package com.android.settingslib.volume.data.repository
+import android.media.MediaRoute2Info
+import android.media.MediaRouter2Manager
+import android.media.RoutingSessionInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.volume.data.model.RoutingSession
+import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -32,6 +37,10 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -44,10 +53,12 @@
@Mock private lateinit var localMediaManager: LocalMediaManager
@Mock private lateinit var mediaDevice1: MediaDevice
@Mock private lateinit var mediaDevice2: MediaDevice
+ @Mock private lateinit var mediaRouter2Manager: MediaRouter2Manager
@Captor
private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
+ private val intentsReceiver = FakeAudioManagerIntentsReceiver()
private val testScope = TestScope()
private lateinit var underTest: LocalMediaRepository
@@ -58,7 +69,9 @@
underTest =
LocalMediaRepositoryImpl(
+ intentsReceiver,
localMediaManager,
+ mediaRouter2Manager,
testScope.backgroundScope,
testScope.testScheduler,
)
@@ -97,4 +110,78 @@
assertThat(currentConnectedDevice).isEqualTo(mediaDevice1)
}
}
+
+ @Test
+ fun kek() {
+ testScope.runTest {
+ `when`(localMediaManager.remoteRoutingSessions)
+ .thenReturn(
+ listOf(
+ testRoutingSessionInfo1,
+ testRoutingSessionInfo2,
+ testRoutingSessionInfo3,
+ )
+ )
+ `when`(localMediaManager.shouldEnableVolumeSeekBar(any())).then {
+ (it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo1
+ }
+ `when`(mediaRouter2Manager.getTransferableRoutes(any<RoutingSessionInfo>())).then {
+ if ((it.arguments[0] as RoutingSessionInfo) == testRoutingSessionInfo2) {
+ return@then listOf(mock(MediaRoute2Info::class.java))
+ }
+ emptyList<MediaRoute2Info>()
+ }
+ var remoteRoutingSessions: Collection<RoutingSession>? = null
+ underTest.remoteRoutingSessions
+ .onEach { remoteRoutingSessions = it }
+ .launchIn(backgroundScope)
+
+ runCurrent()
+
+ assertThat(remoteRoutingSessions)
+ .containsExactlyElementsIn(
+ listOf(
+ RoutingSession(
+ routingSessionInfo = testRoutingSessionInfo1,
+ isVolumeSeekBarEnabled = true,
+ isMediaOutputDisabled = true,
+ ),
+ RoutingSession(
+ routingSessionInfo = testRoutingSessionInfo2,
+ isVolumeSeekBarEnabled = false,
+ isMediaOutputDisabled = false,
+ ),
+ RoutingSession(
+ routingSessionInfo = testRoutingSessionInfo3,
+ isVolumeSeekBarEnabled = false,
+ isMediaOutputDisabled = true,
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun adjustSessionVolume_adjusts() {
+ testScope.runTest {
+ var volume = 0
+ `when`(localMediaManager.adjustSessionVolume(anyString(), anyInt())).then {
+ volume = it.arguments[1] as Int
+ Unit
+ }
+
+ underTest.adjustSessionVolume("test_session", 10)
+
+ assertThat(volume).isEqualTo(10)
+ }
+ }
+
+ private companion object {
+ val testRoutingSessionInfo1 =
+ RoutingSessionInfo.Builder("id_1", "test.pkg.1").addSelectedRoute("route_1").build()
+ val testRoutingSessionInfo2 =
+ RoutingSessionInfo.Builder("id_2", "test.pkg.2").addSelectedRoute("route_2").build()
+ val testRoutingSessionInfo3 =
+ RoutingSessionInfo.Builder("id_3", "test.pkg.3").addSelectedRoute("route_3").build()
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
index f07b1bff..430d733 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
@@ -16,9 +16,6 @@
package com.android.settingslib.volume.data.repository
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
@@ -29,6 +26,7 @@
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -52,10 +50,8 @@
@SmallTest
class MediaControllerRepositoryImplTest {
- @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
@Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothCallback>
- @Mock private lateinit var context: Context
@Mock private lateinit var mediaSessionManager: MediaSessionManager
@Mock private lateinit var localBluetoothManager: LocalBluetoothManager
@Mock private lateinit var eventManager: BluetoothEventManager
@@ -70,6 +66,7 @@
@Mock private lateinit var localPlaybackInfo: PlaybackInfo
private val testScope = TestScope()
+ private val intentsReceiver = FakeAudioManagerIntentsReceiver()
private lateinit var underTest: MediaControllerRepository
@@ -97,7 +94,7 @@
underTest =
MediaControllerRepositoryImpl(
- context,
+ intentsReceiver,
mediaSessionManager,
localBluetoothManager,
testScope.backgroundScope,
@@ -124,7 +121,7 @@
.launchIn(backgroundScope)
runCurrent()
- triggerDevicesChange()
+ intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
triggerOnAudioModeChanged()
runCurrent()
@@ -149,7 +146,7 @@
.launchIn(backgroundScope)
runCurrent()
- triggerDevicesChange()
+ intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
triggerOnAudioModeChanged()
runCurrent()
@@ -157,22 +154,19 @@
}
}
- private fun triggerDevicesChange() {
- verify(context).registerReceiver(receiverCaptor.capture(), any())
- receiverCaptor.value.onReceive(context, Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
- }
-
private fun triggerOnAudioModeChanged() {
verify(eventManager).registerCallback(callbackCaptor.capture())
callbackCaptor.value.onAudioModeChanged()
}
private companion object {
- val statePlaying =
+ val statePlaying: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0, 0f).build()
- val stateError = PlaybackState.Builder().setState(PlaybackState.STATE_ERROR, 0, 0f).build()
- val stateStopped =
+ val stateError: PlaybackState =
+ PlaybackState.Builder().setState(PlaybackState.STATE_ERROR, 0, 0f).build()
+ val stateStopped: PlaybackState =
PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0, 0f).build()
- val stateNone = PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 0, 0f).build()
+ val stateNone: PlaybackState =
+ PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 0, 0f).build()
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt
new file mode 100644
index 0000000..530690a
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.settingslib.volume.shared
+
+import android.content.Intent
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+
+class FakeAudioManagerIntentsReceiver : AudioManagerIntentsReceiver {
+
+ private val mutableIntents = MutableSharedFlow<Intent>()
+ override val intents: SharedFlow<Intent> = mutableIntents.asSharedFlow()
+
+ suspend fun triggerIntent(intent: Intent) {
+ mutableIntents.emit(intent)
+ }
+
+ suspend fun triggerIntent(action: String) {
+ triggerIntent(Intent(action))
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8ad5f24..dc8116d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -261,6 +261,7 @@
Settings.Secure.CREDENTIAL_SERVICE,
Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
Settings.Secure.EVEN_DIMMER_ACTIVATED,
- Settings.Secure.EVEN_DIMMER_MIN_NITS
+ Settings.Secure.EVEN_DIMMER_MIN_NITS,
+ Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d854df38..fabdafc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -416,5 +416,6 @@
VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR);
VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR);
VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
+ VALIDATORS.put(Secure.STYLUS_POINTER_ICON_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e4a762a..bc07836 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2601,6 +2601,9 @@
p.end(soundsToken);
dumpSetting(s, p,
+ Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
+ SecureSettingsProto.STYLUS_POINTER_ICON_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e99fcc9..84ef6e5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -914,6 +914,9 @@
<!-- Permission required for Cts test ScreenRecordingCallbackTests -->
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
+ <!-- Permissions required for CTS test - GrammaticalInflectionManagerTest -->
+ <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
similarity index 85%
rename from packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 4973caf..c489795 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -45,13 +45,13 @@
import com.android.internal.policy.ScreenDecorationsUtils
import kotlin.math.roundToInt
-private const val TAG = "ActivityLaunchAnimator"
+private const val TAG = "ActivityTransitionAnimator"
/**
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityLaunchAnimator(
+class ActivityTransitionAnimator(
/** The animator used when animating a View into an app. */
private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
@@ -97,7 +97,7 @@
)
// TODO(b/288507023): Remove this flag.
- @JvmField val DEBUG_LAUNCH_ANIMATION = Build.IS_DEBUGGABLE
+ @JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE
private val DEFAULT_TRANSITION_ANIMATOR = TransitionAnimator(TIMINGS, INTERPOLATORS)
private val DEFAULT_DIALOG_TO_APP_ANIMATOR =
@@ -113,13 +113,13 @@
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
/** The time we wait before timing out the remote animation after starting the intent. */
- private const val LAUNCH_TIMEOUT = 1_000L
+ private const val TRANSITION_TIMEOUT = 1_000L
/**
* The time we wait before we Log.wtf because the remote animation was neither started or
* cancelled by WM.
*/
- private const val LONG_LAUNCH_TIMEOUT = 5_000L
+ private const val LONG_TRANSITION_TIMEOUT = 5_000L
}
/**
@@ -134,20 +134,20 @@
/** Top-level listener that can be used to notify all registered [listeners]. */
private val lifecycleListener =
object : Listener {
- override fun onLaunchAnimationStart() {
- listeners.forEach { it.onLaunchAnimationStart() }
+ override fun onTransitionAnimationStart() {
+ listeners.forEach { it.onTransitionAnimationStart() }
}
- override fun onLaunchAnimationEnd() {
- listeners.forEach { it.onLaunchAnimationEnd() }
+ override fun onTransitionAnimationEnd() {
+ listeners.forEach { it.onTransitionAnimationEnd() }
}
- override fun onLaunchAnimationProgress(linearProgress: Float) {
- listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
+ override fun onTransitionAnimationProgress(linearProgress: Float) {
+ listeners.forEach { it.onTransitionAnimationProgress(linearProgress) }
}
- override fun onLaunchAnimationCancelled() {
- listeners.forEach { it.onLaunchAnimationCancelled() }
+ override fun onTransitionAnimationCancelled() {
+ listeners.forEach { it.onTransitionAnimationCancelled() }
}
}
@@ -188,7 +188,7 @@
val callback =
this.callback
?: throw IllegalStateException(
- "ActivityLaunchAnimator.callback must be set before using this animator"
+ "ActivityTransitionAnimator.callback must be set before using this animator"
)
val runner = createRunner(controller)
val runnerDelegate = runner.delegate!!
@@ -260,7 +260,7 @@
callOnIntentStartedOnMainThread(willAnimate)
}
} else {
- if (DEBUG_LAUNCH_ANIMATION) {
+ if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
TAG,
"Calling controller.onIntentStarted(willAnimate=$willAnimate) " +
@@ -293,7 +293,7 @@
}
}
- /** Add a [Listener] that can listen to launch animations. */
+ /** Add a [Listener] that can listen to transition animations. */
fun addListener(listener: Listener) {
listeners.add(listener)
}
@@ -340,24 +340,24 @@
}
interface Listener {
- /** Called when an activity launch animation started. */
- fun onLaunchAnimationStart() {}
+ /** Called when an activity transition animation started. */
+ fun onTransitionAnimationStart() {}
/**
- * Called when an activity launch animation is finished. This will be called if and only if
- * [onLaunchAnimationStart] was called earlier.
+ * Called when an activity transition animation is finished. This will be called if and only
+ * if [onTransitionAnimationStart] was called earlier.
*/
- fun onLaunchAnimationEnd() {}
+ fun onTransitionAnimationEnd() {}
/**
- * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
- * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
- * before the cancellation.
+ * The animation was cancelled. Note that [onTransitionAnimationEnd] will still be called
+ * after this if the animation was already started, i.e. if [onTransitionAnimationStart] was
+ * called before the cancellation.
*/
- fun onLaunchAnimationCancelled() {}
+ fun onTransitionAnimationCancelled() {}
- /** Called when an activity launch animation made progress. */
- fun onLaunchAnimationProgress(linearProgress: Float) {}
+ /** Called when an activity transition animation made progress. */
+ fun onTransitionAnimationProgress(linearProgress: Float) {}
}
/**
@@ -383,9 +383,10 @@
// issues.
if (view !is LaunchableView) {
throw IllegalArgumentException(
- "An ActivityLaunchAnimator.Controller was created from a View that does " +
- "not implement LaunchableView. This can lead to subtle bugs where the" +
- " visibility of the View we are launching from is not what we expected."
+ "An ActivityTransitionAnimator.Controller was created from a View that " +
+ "does not implement LaunchableView. This can lead to subtle bugs " +
+ "where the visibility of the View we are launching from is not what " +
+ "we expected."
)
}
@@ -411,11 +412,11 @@
get() = false
/**
- * Whether the expandable controller by this [Controller] is below the launching window that
- * is going to be animated.
+ * Whether the expandable controller by this [Controller] is below the window that is going
+ * to be animated.
*
- * This should be `false` when launching an app from the shade or status bar, given that
- * they are drawn above all apps. This is usually `true` when using this launcher in a
+ * This should be `false` when animating an app from or to the shade or status bar, given
+ * that they are drawn above all apps. This is usually `true` when using this animator in a
* normal app or a launcher, that are drawn below the animating activity/window.
*/
val isBelowAnimatingWindow: Boolean
@@ -432,10 +433,11 @@
* after this if the animation was already started, i.e. if [onTransitionAnimationStart] was
* called before the cancellation.
*
- * If this launch animation affected the occlusion state of the keyguard, WM will provide us
- * with [newKeyguardOccludedState] so that we can set the occluded state appropriately.
+ * If this transition animation affected the occlusion state of the keyguard, WM will
+ * provide us with [newKeyguardOccludedState] so that we can set the occluded state
+ * appropriately.
*/
- fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
+ fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
}
/**
@@ -449,24 +451,24 @@
) : Listener {
var cancelled = false
- override fun onLaunchAnimationStart() {
- delegate?.onLaunchAnimationStart()
+ override fun onTransitionAnimationStart() {
+ delegate?.onTransitionAnimationStart()
}
- override fun onLaunchAnimationProgress(linearProgress: Float) {
- delegate?.onLaunchAnimationProgress(linearProgress)
+ override fun onTransitionAnimationProgress(linearProgress: Float) {
+ delegate?.onTransitionAnimationProgress(linearProgress)
}
- override fun onLaunchAnimationEnd() {
- delegate?.onLaunchAnimationEnd()
+ override fun onTransitionAnimationEnd() {
+ delegate?.onTransitionAnimationEnd()
if (!cancelled) {
onAnimationComplete.invoke()
}
}
- override fun onLaunchAnimationCancelled() {
+ override fun onTransitionAnimationCancelled() {
cancelled = true
- delegate?.onLaunchAnimationCancelled()
+ delegate?.onTransitionAnimationCancelled()
onAnimationComplete.invoke()
}
}
@@ -475,7 +477,7 @@
inner class Runner(
controller: Controller,
callback: Callback,
- /** The animator to use to animate the window launch. */
+ /** The animator to use to animate the window transition. */
transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
/** Listener for animation lifecycle events. */
listener: Listener? = null
@@ -543,7 +545,7 @@
private val callback: Callback,
/** Listener for animation lifecycle events. */
private val listener: Listener? = null,
- /** The animator to use to animate the window launch. */
+ /** The animator to use to animate the window transition. */
private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
/**
@@ -574,8 +576,8 @@
private var animation: TransitionAnimator.Animation? = null
/**
- * A timeout to cancel the launch animation if the remote animation is not started or
- * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started.
+ * A timeout to cancel the transition animation if the remote animation is not started or
+ * cancelled within [TRANSITION_TIMEOUT] milliseconds after the intent was started.
*
* Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
* it will be automatically converted when posted and we wouldn't be able to remove it after
@@ -585,21 +587,22 @@
/**
* A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't
- * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was
+ * started or cancelled within [LONG_TRANSITION_TIMEOUT] milliseconds after the intent was
* started.
*/
private var onLongTimeout = Runnable {
Log.wtf(
TAG,
- "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT"
+ "The remote animation was neither cancelled or started within " +
+ "$LONG_TRANSITION_TIMEOUT"
)
}
@UiThread
internal fun postTimeouts() {
if (timeoutHandler != null) {
- timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT)
- timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT)
+ timeoutHandler.postDelayed(onTimeout, TRANSITION_TIMEOUT)
+ timeoutHandler.postDelayed(onLongTimeout, LONG_TRANSITION_TIMEOUT)
}
}
@@ -670,14 +673,14 @@
Log.i(TAG, "Aborting the animation as no window is opening")
iCallback?.invoke()
- if (DEBUG_LAUNCH_ANIMATION) {
+ if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
TAG,
- "Calling controller.onLaunchAnimationCancelled() [no window opening]"
+ "Calling controller.onTransitionAnimationCancelled() [no window opening]"
)
}
- controller.onLaunchAnimationCancelled()
- listener?.onLaunchAnimationCancelled()
+ controller.onTransitionAnimationCancelled()
+ listener?.onTransitionAnimationCancelled()
return
}
@@ -720,27 +723,29 @@
val controller =
object : Controller by delegate {
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
- listener?.onLaunchAnimationStart()
+ listener?.onTransitionAnimationStart()
- if (DEBUG_LAUNCH_ANIMATION) {
+ if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
TAG,
- "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
- "$isExpandingFullyAbove) [controller=$delegate]"
+ "Calling controller.onTransitionAnimationStart(" +
+ "isExpandingFullyAbove=$isExpandingFullyAbove) " +
+ "[controller=$delegate]"
)
}
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- listener?.onLaunchAnimationEnd()
+ listener?.onTransitionAnimationEnd()
iCallback?.invoke()
- if (DEBUG_LAUNCH_ANIMATION) {
+ if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
TAG,
- "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
- "$isExpandingFullyAbove) [controller=$delegate]"
+ "Calling controller.onTransitionAnimationEnd(" +
+ "isExpandingFullyAbove=$isExpandingFullyAbove) " +
+ "[controller=$delegate]"
)
}
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
@@ -758,7 +763,7 @@
}
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
- listener?.onLaunchAnimationProgress(linearProgress)
+ listener?.onTransitionAnimationProgress(linearProgress)
delegate.onTransitionAnimationProgress(state, progress, linearProgress)
}
}
@@ -904,7 +909,7 @@
}
private fun onAnimationTimedOut() {
- // The remote animation was cancelled by WM, so we already cancelled the launch
+ // The remote animation was cancelled by WM, so we already cancelled the transition
// animation.
if (cancelled) {
return
@@ -913,18 +918,21 @@
Log.w(TAG, "Remote animation timed out")
timedOut = true
- if (DEBUG_LAUNCH_ANIMATION) {
- Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
+ if (DEBUG_TRANSITION_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onTransitionAnimationCancelled() [animation timed out]"
+ )
}
- controller.onLaunchAnimationCancelled()
- listener?.onLaunchAnimationCancelled()
+ controller.onTransitionAnimationCancelled()
+ listener?.onTransitionAnimationCancelled()
}
@UiThread
override fun onAnimationCancelled() {
removeTimeouts()
- // The short timeout happened, so we already cancelled the launch animation.
+ // The short timeout happened, so we already cancelled the transition animation.
if (timedOut) {
return
}
@@ -934,14 +942,15 @@
animation?.cancel()
- if (DEBUG_LAUNCH_ANIMATION) {
+ if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
TAG,
- "Calling controller.onLaunchAnimationCancelled() [remote animation cancelled]",
+ "Calling controller.onTransitionAnimationCancelled() [remote animation " +
+ "cancelled]",
)
}
- controller.onLaunchAnimationCancelled()
- listener?.onLaunchAnimationCancelled()
+ controller.onTransitionAnimationCancelled()
+ listener?.onTransitionAnimationCancelled()
}
private fun IRemoteAnimationFinishedCallback.invoke() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
index b879ba0..a53ab62 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -17,10 +17,10 @@
package com.android.systemui.animation
/**
- * A base class to easily create an implementation of [ActivityLaunchAnimator.Controller] which
+ * A base class to easily create an implementation of [ActivityTransitionAnimator.Controller] which
* delegates most of its call to [delegate]. This is mostly useful for Java code which can't easily
* create such a delegated class.
*/
open class DelegateLaunchAnimatorController(
- protected val delegate: ActivityLaunchAnimator.Controller
-) : ActivityLaunchAnimator.Controller by delegate
+ protected val delegate: ActivityTransitionAnimator.Controller
+) : ActivityTransitionAnimator.Controller by delegate
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9a36960..ed7f31c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -62,13 +62,14 @@
private val isForTesting: Boolean = false,
) {
private companion object {
- private val TIMINGS = ActivityLaunchAnimator.TIMINGS
+ private val TIMINGS = ActivityTransitionAnimator.TIMINGS
// We use the same interpolator for X and Y axis to make sure the dialog does not move out
// of the screen bounds during the animation.
private val INTERPOLATORS =
- ActivityLaunchAnimator.INTERPOLATORS.copy(
- positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
+ ActivityTransitionAnimator.INTERPOLATORS.copy(
+ positionXInterpolator =
+ ActivityTransitionAnimator.INTERPOLATORS.positionInterpolator
)
}
@@ -319,9 +320,9 @@
}
/**
- * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the
- * dialog that contains [View]. Note that the dialog must have been shown using this animator,
- * otherwise this method will return null.
+ * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from
+ * the dialog that contains [View]. Note that the dialog must have been shown using this
+ * animator, otherwise this method will return null.
*
* The returned controller will take care of dismissing the dialog at the right time after the
* activity started, when the dialog to app animation is done (or when it is cancelled). If this
@@ -333,7 +334,7 @@
fun createActivityLaunchController(
view: View,
cujType: Int? = null,
- ): ActivityLaunchAnimator.Controller? {
+ ): ActivityTransitionAnimator.Controller? {
val animatedDialog =
openedDialogs.firstOrNull {
it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl
@@ -343,7 +344,7 @@
}
/**
- * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from
+ * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from
* [dialog]. Note that the dialog must have been shown using this animator, otherwise this
* method will return null.
*
@@ -357,7 +358,7 @@
fun createActivityLaunchController(
dialog: Dialog,
cujType: Int? = null,
- ): ActivityLaunchAnimator.Controller? {
+ ): ActivityTransitionAnimator.Controller? {
val animatedDialog = openedDialogs.firstOrNull { it.dialog == dialog } ?: return null
return createActivityLaunchController(animatedDialog, cujType)
}
@@ -365,7 +366,7 @@
private fun createActivityLaunchController(
animatedDialog: AnimatedDialog,
cujType: Int? = null
- ): ActivityLaunchAnimator.Controller? {
+ ): ActivityTransitionAnimator.Controller? {
// At this point, we know that the intent of the caller is to dismiss the dialog to show
// an app, so we disable the exit animation into the source because we will never want to
// run it anyways.
@@ -384,12 +385,12 @@
val dialogContentWithBackground = animatedDialog.dialogContentWithBackground ?: return null
val controller =
- ActivityLaunchAnimator.Controller.fromView(dialogContentWithBackground, cujType)
+ ActivityTransitionAnimator.Controller.fromView(dialogContentWithBackground, cujType)
?: return null
// Wrap the controller into one that will instantly dismiss the dialog when the animation is
// done or dismiss it normally (fading it out) if the animation is cancelled.
- return object : ActivityLaunchAnimator.Controller by controller {
+ return object : ActivityTransitionAnimator.Controller by controller {
override val isDialogLaunch = true
override fun onIntentStarted(willAnimate: Boolean) {
@@ -400,8 +401,8 @@
}
}
- override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
- controller.onLaunchAnimationCancelled()
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ controller.onTransitionAnimationCancelled()
enableDialogDismiss()
dialog.dismiss()
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
index c49a487..2ba5948 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
@@ -21,14 +21,14 @@
/** A piece of UI that can be expanded into a Dialog or an Activity. */
interface Expandable {
/**
- * Create an [ActivityLaunchAnimator.Controller] that can be used to expand this [Expandable]
- * into an Activity, or return `null` if this [Expandable] should not be animated (e.g. if it is
- * currently not attached or visible).
+ * Create an [ActivityTransitionAnimator.Controller] that can be used to expand this
+ * [Expandable] into an Activity, or return `null` if this [Expandable] should not be animated
+ * (e.g. if it is currently not attached or visible).
*
* @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
* associated to the launch that will use this controller.
*/
- fun activityLaunchController(cujType: Int? = null): ActivityLaunchAnimator.Controller?
+ fun activityLaunchController(cujType: Int? = null): ActivityTransitionAnimator.Controller?
/**
* Create a [DialogLaunchAnimator.Controller] that can be used to expand this [Expandable] into
@@ -49,8 +49,8 @@
return object : Expandable {
override fun activityLaunchController(
cujType: Int?,
- ): ActivityLaunchAnimator.Controller? {
- return ActivityLaunchAnimator.Controller.fromView(view, cujType)
+ ): ActivityTransitionAnimator.Controller? {
+ return ActivityTransitionAnimator.Controller.fromView(view, cujType)
}
override fun dialogLaunchController(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 03f10f9..f7148d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -42,15 +42,15 @@
private const val TAG = "GhostedViewLaunchAnimatorController"
/**
- * A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView]
- * of [ghostedView] as well as an expandable background view, which are drawn and animated instead
- * of the ghosted view.
+ * A base implementation of [ActivityTransitionAnimator.Controller] which creates a
+ * [ghost][GhostView] of [ghostedView] as well as an expandable background view, which are drawn and
+ * animated instead of the ghosted view.
*
* Important: [ghostedView] must be attached to a [ViewGroup] when calling this function and during
* the animation. It must also implement [LaunchableView], otherwise an exception will be thrown
* during this controller instantiation.
*
- * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
+ * Note: Avoid instantiating this directly and call [ActivityTransitionAnimator.Controller.fromView]
* whenever possible instead.
*/
open class GhostedViewLaunchAnimatorController
@@ -63,7 +63,7 @@
private val cujType: Int? = null,
private var interactionJankMonitor: InteractionJankMonitor =
InteractionJankMonitor.getInstance(),
-) : ActivityLaunchAnimator.Controller {
+) : ActivityTransitionAnimator.Controller {
/** The container to which we will add the ghost view and expanding background. */
override var transitionContainer = ghostedView.rootView as ViewGroup
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 1020263..84e5725 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -40,7 +40,7 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
@@ -135,7 +135,7 @@
object : Expandable {
override fun activityLaunchController(
cujType: Int?,
- ): ActivityLaunchAnimator.Controller? {
+ ): ActivityTransitionAnimator.Controller? {
if (!isComposed.value) {
return null
}
@@ -176,7 +176,7 @@
linearProgress: Float
) {
// We copy state given that it's always the same object that is mutated by
- // ActivityLaunchAnimator.
+ // ActivityTransitionAnimator.
animatorState.value =
TransitionAnimator.State(
state.top,
@@ -256,11 +256,11 @@
}
}
- /** Create an [ActivityLaunchAnimator.Controller] that can be used to animate activities. */
- private fun activityController(cujType: Int?): ActivityLaunchAnimator.Controller {
+ /** Create an [ActivityTransitionAnimator.Controller] that can be used to animate activities. */
+ private fun activityController(cujType: Int?): ActivityTransitionAnimator.Controller {
val delegate = transitionController()
return object :
- ActivityLaunchAnimator.Controller, TransitionAnimator.Controller by delegate {
+ ActivityTransitionAnimator.Controller, TransitionAnimator.Controller by delegate {
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index ff53ff2..378a1e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -34,6 +34,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -43,23 +44,21 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val viewModel: LockscreenSceneViewModel,
+ viewModel: LockscreenSceneViewModel,
private val lockscreenContent: Lazy<LockscreenContent>,
) : ComposableScene {
override val key = SceneKey.Lockscreen
override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
- viewModel.upDestinationSceneKey
- .map { pageKey ->
- destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
- }
+ combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair)
+ .map { (upKey, leftKey) -> destinationScenes(up = upKey, left = leftKey) }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue =
destinationScenes(
up = viewModel.upDestinationSceneKey.value,
- left = viewModel.leftDestinationSceneKey,
+ left = viewModel.leftDestinationSceneKey.value,
)
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index d70f82f..ef6ae2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -19,6 +19,7 @@
import android.util.Log
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
@@ -140,6 +141,8 @@
) {
val density = LocalDensity.current
val screenCornerRadius = LocalScreenCornerRadius.current
+ val scrollState = rememberScrollState()
+ val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
val expansionFraction by viewModel.expandFraction.collectAsState(0f)
val navBarHeight =
@@ -180,11 +183,28 @@
// if contentHeight drops below minimum visible scrim height while scrim is
// expanded, reset scrim offset.
- LaunchedEffect(contentHeight, screenHeight, maxScrimTop, scrimOffset) {
+ LaunchedEffect(contentHeight, scrimOffset) {
snapshotFlow { contentHeight.value < minVisibleScrimHeight() && scrimOffset.value < 0f }
.collect { shouldCollapse -> if (shouldCollapse) scrimOffset.value = 0f }
}
+ // if we receive scroll delta from NSSL, offset the scrim and placeholder accordingly.
+ LaunchedEffect(syntheticScroll, scrimOffset, scrollState) {
+ snapshotFlow { syntheticScroll.value }
+ .collect { delta ->
+ val minOffset = minScrimOffset()
+ if (scrimOffset.value > minOffset) {
+ val remainingDelta = (minOffset - (scrimOffset.value - delta)).coerceAtLeast(0f)
+ scrimOffset.value = (scrimOffset.value - delta).coerceAtLeast(minOffset)
+ if (remainingDelta > 0f) {
+ scrollState.scrollBy(remainingDelta)
+ }
+ } else {
+ scrollState.scrollTo(delta.roundToInt())
+ }
+ }
+ }
+
Box(
modifier =
modifier
@@ -260,7 +280,7 @@
)
}
)
- .verticalScroll(rememberScrollState())
+ .verticalScroll(scrollState)
.fillMaxWidth()
.height { (contentHeight.value + navBarHeight).roundToInt() },
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index c027c49..de8f2ec 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -18,21 +18,21 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.MovableElementScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
+import com.android.compose.modifiers.thenIf
import com.android.compose.theme.colorAttr
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
@@ -44,9 +44,16 @@
import com.android.systemui.scene.ui.composable.Shade
object QuickSettings {
+ private val SCENES =
+ setOf(
+ QuickSettingsSceneKey,
+ Shade,
+ )
+
object Elements {
// TODO RENAME
- val Content = ElementKey("QuickSettingsContent")
+ val Content =
+ ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
val FooterActions = ElementKey("QuickSettingsFooterActions")
}
@@ -86,14 +93,22 @@
*/
@Composable
fun SceneScope.QuickSettings(
- modifier: Modifier = Modifier,
qsSceneAdapter: QSSceneAdapter,
+ heightProvider: () -> Int,
+ modifier: Modifier = Modifier,
) {
val contentState = stateForQuickSettingsContent()
MovableElement(
key = QuickSettings.Elements.Content,
- modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 300.dp)
+ modifier =
+ modifier.fillMaxWidth().layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ // Use the height of the correct view based on the scene it is being composed in
+ val height = heightProvider()
+
+ layout(placeable.width, height) { placeable.placeRelative(0, 0) }
+ }
) {
content { QuickSettingsContent(qsSceneAdapter = qsSceneAdapter, contentState) }
}
@@ -118,15 +133,7 @@
qsView?.let { view ->
Box(
modifier =
- modifier
- .fillMaxWidth()
- .then(
- if (isCustomizing) {
- Modifier.fillMaxHeight()
- } else {
- Modifier.wrapContentHeight()
- }
- )
+ modifier.fillMaxWidth().thenIf(isCustomizing) { Modifier.fillMaxHeight() }
) {
AndroidView(
modifier = Modifier.fillMaxWidth().background(colorAttr(R.attr.underSurface)),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 969dec3..1cbc992 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -213,8 +213,9 @@
Spacer(modifier = Modifier.height(16.dp))
// This view has its own horizontal padding
QuickSettings(
- modifier = Modifier.sysuiResTag("expanded_qs_scroll_view"),
viewModel.qsSceneAdapter,
+ { viewModel.qsSceneAdapter.qsHeight },
+ modifier = Modifier.sysuiResTag("expanded_qs_scroll_view"),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 677df7e..cac35cb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -189,8 +189,8 @@
)
)
QuickSettings(
- modifier = Modifier.height(130.dp),
viewModel.qsSceneAdapter,
+ { viewModel.qsSceneAdapter.qqsHeight },
)
if (viewModel.isMediaVisible()) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index c4bcb53..f86342c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -31,6 +31,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -102,13 +103,15 @@
.thenReturn(mOkButton);
when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor =
+ new KeyguardKeyboardInteractor(new FakeKeyboardRepository());
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
mEmergencyButtonController, mFalsingCollector, featureFlags,
- mSelectedUserInteractor, new FakeKeyboardRepository()) {
+ mSelectedUserInteractor, keyguardKeyboardInteractor) {
@Override
public void onResume(int reason) {
super.onResume(reason);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index f7743e2..259f349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -37,7 +37,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel
@@ -106,7 +106,7 @@
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
- @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+ @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -167,7 +167,7 @@
reason,
controllerCallback,
onTouch,
- activityLaunchAnimator,
+ mActivityTransitionAnimator,
primaryBouncerInteractor,
alternateBouncerInteractor,
isDebuggable,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 90c3c14..529403a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -75,7 +75,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
import com.android.systemui.biometrics.udfps.InteractionEvent;
@@ -203,7 +203,7 @@
@Mock
private SystemUIDialogManager mSystemUIDialogManager;
@Mock
- private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private ActivityTransitionAnimator mActivityTransitionAnimator;
@Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock
@@ -331,7 +331,7 @@
mUnlockedScreenOffAnimationController,
mSystemUIDialogManager,
mLatencyTracker,
- mActivityLaunchAnimator,
+ mActivityTransitionAnimator,
mBiometricExecutor,
mPrimaryBouncerInteractor,
mShadeInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 7d9c2f9..324534f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -26,7 +26,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -70,7 +70,7 @@
protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
protected @Mock SystemUIDialogManager mDialogManager;
protected @Mock UdfpsController mUdfpsController;
- protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+ protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected @Mock ShadeInteractor mShadeInteractor;
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -148,7 +148,7 @@
mUnlockedScreenOffAnimationController,
mDialogManager,
mUdfpsController,
- mActivityLaunchAnimator,
+ mActivityTransitionAnimator,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index bd9ca30..b4e2eab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -16,26 +16,18 @@
package com.android.systemui.communal.data.repository
-import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
@@ -48,37 +40,20 @@
class CommunalRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: CommunalRepositoryImpl
- private lateinit var secureSettings: FakeSettings
- private lateinit var userRepository: FakeUserRepository
-
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneContainerRepository = kosmos.sceneContainerRepository
@Before
fun setUp() {
- secureSettings = FakeSettings()
- userRepository = kosmos.fakeUserRepository
-
- val listOfUserInfo = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(listOfUserInfo)
-
- kosmos.fakeFeatureFlagsClassic.apply { set(Flags.COMMUNAL_SERVICE_ENABLED, true) }
- mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
underTest = createRepositoryImpl(false)
}
private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl {
return CommunalRepositoryImpl(
testScope.backgroundScope,
- testScope.backgroundScope,
- kosmos.testDispatcher,
- kosmos.fakeFeatureFlagsClassic,
kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled },
sceneContainerRepository,
- kosmos.fakeUserRepository,
- secureSettings,
)
}
@@ -159,29 +134,4 @@
assertThat(transitionState)
.isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
}
-
- @Test
- fun communalEnabledState_false_whenGlanceableHubSettingFalse() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, MAIN_USER_INFO.id)
-
- val communalEnabled by collectLastValue(underTest.communalEnabledState)
- assertThat(communalEnabled).isFalse()
- }
-
- @Test
- fun communalEnabledState_true_whenGlanceableHubSettingTrue() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, MAIN_USER_INFO.id)
-
- val communalEnabled by collectLastValue(underTest.communalEnabledState)
- assertThat(communalEnabled).isTrue()
- }
-
- companion object {
- private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
- private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
new file mode 100644
index 0000000..0aca16d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.app.admin.devicePolicyManager
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private lateinit var underTest: CommunalSettingsRepository
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+ setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+ underTest = kosmos.communalSettingsRepository
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun secondaryUserIsInvalid() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
+
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun classicFlagIsDisabled() =
+ testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+ }
+
+ @DisableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun communalHubFlagIsDisabled() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubIsDisabledByUser() =
+ testScope.runTest {
+ kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+ val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
+
+ kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
+ assertThat(enabledState?.enabled).isFalse()
+
+ kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
+ assertThat(enabledState?.enabled).isTrue()
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubIsDisabledByDevicePolicy() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledState?.enabled).isTrue()
+
+ setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY)
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubIsDisabledByUserAndDevicePolicy() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+ assertThat(enabledState?.enabled).isTrue()
+
+ kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+ setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+
+ assertThat(enabledState?.enabled).isFalse()
+ assertThat(enabledState)
+ .containsExactly(
+ DisabledReason.DISABLED_REASON_DEVICE_POLICY,
+ DisabledReason.DISABLED_REASON_USER_SETTING,
+ )
+ }
+
+ private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
+ whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
+ .thenReturn(disabledFlags)
+ kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ )
+ }
+
+ private companion object {
+ val PRIMARY_USER =
+ UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+ val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 6a3fc2a..824733b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -19,6 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -59,7 +60,7 @@
widgetRepository = kosmos.fakeCommunalWidgetRepository
keyguardRepository = kosmos.fakeKeyguardRepository
- communalRepository.setIsCommunalEnabled(false)
+ mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
underTest = kosmos.communalInteractor
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index c5485c5..3ac19e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -23,6 +23,7 @@
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
@@ -41,6 +42,8 @@
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
@@ -109,12 +112,19 @@
whenever(secondaryUser.isMain).thenReturn(false)
userRepository.setUserInfos(listOf(mainUser, secondaryUser))
+ kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+ mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+
underTest = kosmos.communalInteractor
}
@Test
fun communalEnabled_true() =
- testScope.runTest { assertThat(underTest.isCommunalEnabled).isTrue() }
+ testScope.runTest {
+ userRepository.setSelectedUserInfo(mainUser)
+ runCurrent()
+ assertThat(underTest.isCommunalEnabled).isTrue()
+ }
@Test
fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
@@ -125,7 +135,6 @@
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(mainUser)
keyguardRepository.setKeyguardShowing(true)
- communalRepository.setCommunalEnabledState(true)
assertThat(isAvailable).isTrue()
}
@@ -139,7 +148,6 @@
keyguardRepository.setIsEncryptedOrLockdown(true)
userRepository.setSelectedUserInfo(mainUser)
keyguardRepository.setKeyguardShowing(true)
- communalRepository.setCommunalEnabledState(true)
assertThat(isAvailable).isFalse()
}
@@ -153,7 +161,6 @@
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(secondaryUser)
keyguardRepository.setKeyguardShowing(true)
- communalRepository.setCommunalEnabledState(true)
assertThat(isAvailable).isFalse()
}
@@ -167,7 +174,6 @@
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(mainUser)
keyguardRepository.setDreaming(true)
- communalRepository.setCommunalEnabledState(true)
assertThat(isAvailable).isTrue()
}
@@ -175,13 +181,14 @@
@Test
fun isCommunalAvailable_communalDisabled_false() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
+
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(mainUser)
keyguardRepository.setKeyguardShowing(true)
- communalRepository.setCommunalEnabledState(false)
assertThat(isAvailable).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 6c87e0f..ceb7fac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -22,12 +22,15 @@
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
@@ -35,11 +38,13 @@
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -62,6 +67,8 @@
userRepository = kosmos.fakeUserRepository
userRepository.setUserInfos(listOf(MAIN_USER_INFO))
+ kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+ mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
underTest = kosmos.communalTutorialInteractor
}
@@ -127,6 +134,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
communalRepository.setIsCommunalHubShowing(true)
@@ -139,6 +147,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
communalRepository.setIsCommunalHubShowing(true)
@@ -151,6 +160,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
communalRepository.setIsCommunalHubShowing(true)
@@ -163,6 +173,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
communalRepository.setIsCommunalHubShowing(false)
@@ -175,6 +186,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalRepository.setIsCommunalHubShowing(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
@@ -188,6 +200,7 @@
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
+ userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalRepository.setIsCommunalHubShowing(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
@@ -198,14 +211,11 @@
private suspend fun setCommunalAvailable(available: Boolean) {
if (available) {
- communalRepository.setIsCommunalEnabled(true)
- communalRepository.setCommunalEnabledState(true)
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(MAIN_USER_INFO)
keyguardRepository.setKeyguardShowing(true)
} else {
- communalRepository.setIsCommunalEnabled(false)
- communalRepository.setCommunalEnabledState(false)
+ keyguardRepository.setIsEncryptedOrLockdown(true)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 73d3091..f70b6a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -22,12 +22,12 @@
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
@@ -37,6 +37,8 @@
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
@@ -92,7 +94,8 @@
mediaRepository = kosmos.fakeCommunalMediaRepository
userRepository = kosmos.fakeUserRepository
- kosmos.fakeCommunalRepository.setCommunalEnabledState(true)
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
underTest =
CommunalViewModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 032d76f..8488843 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -19,12 +19,15 @@
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
@@ -33,6 +36,7 @@
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -62,6 +66,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+ kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+ mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
appWidgetIdToRemove = MutableSharedFlow()
whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
@@ -169,7 +175,8 @@
fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
fakeKeyguardRepository.setKeyguardShowing(true)
- fakeCommunalRepository.setCommunalEnabledState(available)
+ val settingsValue = if (available) 1 else 0
+ fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
}
private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index a613ad8..0768340 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -23,14 +23,14 @@
import android.service.quickaccesswallet.WalletCard
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -76,26 +76,28 @@
}
@Test
- fun affordance_keyguardShowing_hasWalletCard_visibleModel() = testScope.runTest {
- setUpState()
+ fun affordance_keyguardShowing_hasWalletCard_visibleModel() =
+ testScope.runTest {
+ setUpState()
- val latest by collectLastValue(underTest.lockScreenState)
+ val latest by collectLastValue(underTest.lockScreenState)
- val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
- assertThat(visibleModel.icon)
- .isEqualTo(
- Icon.Loaded(
- drawable = ICON,
- contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
)
- )
- }
+ }
@Test
- fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = testScope.runTest {
+ fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+ testScope.runTest {
setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
val latest by collectLastValue(underTest.lockScreenState)
@@ -104,54 +106,58 @@
}
@Test
- fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = testScope.runTest {
- setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+ fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+ testScope.runTest {
+ setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
- val latest by collectLastValue(underTest.lockScreenState)
+ val latest by collectLastValue(underTest.lockScreenState)
- val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
- assertThat(visibleModel.icon)
- .isEqualTo(
- Icon.Loaded(
- drawable = ICON,
- contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
)
- )
- }
+ }
@Test
- fun affordance_walletFeatureNotEnabled_modelIsNone() = testScope.runTest {
- setUpState(isWalletFeatureAvailable = false)
+ fun affordance_walletFeatureNotEnabled_modelIsNone() =
+ testScope.runTest {
+ setUpState(isWalletFeatureAvailable = false)
- val latest by collectLastValue(underTest.lockScreenState)
+ val latest by collectLastValue(underTest.lockScreenState)
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun affordance_queryNotSuccessful_modelIsNone() = testScope.runTest {
- setUpState(isWalletQuerySuccessful = false)
+ fun affordance_queryNotSuccessful_modelIsNone() =
+ testScope.runTest {
+ setUpState(isWalletQuerySuccessful = false)
- val latest by collectLastValue(underTest.lockScreenState)
+ val latest by collectLastValue(underTest.lockScreenState)
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun affordance_noSelectedCard_modelIsNone() = testScope.runTest {
- setUpState(hasSelectedCard = false)
+ fun affordance_noSelectedCard_modelIsNone() =
+ testScope.runTest {
+ setUpState(hasSelectedCard = false)
- val latest by collectLastValue(underTest.lockScreenState)
+ val latest by collectLastValue(underTest.lockScreenState)
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
fun onQuickAffordanceTriggered() {
- val animationController: ActivityLaunchAnimator.Controller = mock()
+ val animationController: ActivityTransitionAnimator.Controller = mock()
val expandable: Expandable = mock {
whenever(this.activityLaunchController()).thenReturn(animationController)
}
@@ -167,42 +173,46 @@
}
@Test
- fun getPickerScreenState_default() = testScope.runTest {
- setUpState()
+ fun getPickerScreenState_default() =
+ testScope.runTest {
+ setUpState()
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
- }
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
+ }
@Test
- fun getPickerScreenState_unavailable() = testScope.runTest {
- setUpState(
- isWalletServiceAvailable = false,
- )
+ fun getPickerScreenState_unavailable() =
+ testScope.runTest {
+ setUpState(
+ isWalletServiceAvailable = false,
+ )
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
@Test
- fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = testScope.runTest {
- setUpState(
- isWalletFeatureAvailable = false,
- )
+ fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() =
+ testScope.runTest {
+ setUpState(
+ isWalletFeatureAvailable = false,
+ )
- assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
+ }
@Test
- fun getPickerScreenState_disabledWhenThereIsNoCard() = testScope.runTest {
- setUpState(
- hasSelectedCard = false,
- )
+ fun getPickerScreenState_disabledWhenThereIsNoCard() =
+ testScope.runTest {
+ setUpState(
+ hasSelectedCard = false,
+ )
- assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
+ }
private fun setUpState(
isWalletFeatureAvailable: Boolean = true,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 0543bc2..d52696a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -20,6 +20,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -68,6 +69,8 @@
@Before
fun setUp() {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
MockitoAnnotations.initMocks(this)
whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
kosmos.burnInInteractor = burnInInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 2de013b..c23ec22 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -116,6 +116,23 @@
}
@Test
+ fun iconContainer_isNotVisible_onKeyguard_dontShowWhenGoneToAodTransitionRunning() =
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope,
+ )
+ whenever(screenOffAnimationController.shouldShowAodIconsWhenShade()).thenReturn(false)
+ runCurrent()
+
+ assertThat(isVisible?.value).isFalse()
+ assertThat(isVisible?.isAnimating).isFalse()
+ }
+
+ @Test
fun iconContainer_isVisible_bypassEnabled() =
testScope.runTest {
val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 4595fbf..7261723 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,22 +18,28 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,9 +55,7 @@
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val underTest by lazy {
- createLockscreenSceneViewModel()
- }
+ private val underTest by lazy { createLockscreenSceneViewModel() }
@Test
fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -80,29 +84,37 @@
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
+ @EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun leftTransitionSceneKey_communalIsEnabled_communal() =
testScope.runTest {
- kosmos.fakeCommunalRepository.setIsCommunalEnabled(true)
- val underTest = createLockscreenSceneViewModel()
-
- assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(PRIMARY_USER))
+ setSelectedUserInfo(PRIMARY_USER)
+ }
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+ assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
}
+ @DisableFlags(FLAG_COMMUNAL_HUB)
@Test
fun leftTransitionSceneKey_communalIsDisabled_null() =
testScope.runTest {
- kosmos.fakeCommunalRepository.setIsCommunalEnabled(false)
- val underTest = createLockscreenSceneViewModel()
-
- assertThat(underTest.leftDestinationSceneKey).isNull()
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(PRIMARY_USER))
+ setSelectedUserInfo(PRIMARY_USER)
+ }
+ kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+ assertThat(leftDestinationSceneKey).isNull()
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
return LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = kosmos.deviceEntryInteractor,
- communalInteractor = kosmos.communalInteractor,
+ communalSettingsInteractor = kosmos.communalSettingsInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -110,4 +122,9 @@
notifications = kosmos.notificationsPlaceholderViewModel,
)
}
+
+ private companion object {
+ val PRIMARY_USER =
+ UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 42200a3..51f8b11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -61,7 +61,7 @@
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
- private val qsFlexiglassAdapter = FakeQSSceneAdapter { mock() }
+ private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
private val footerActionsViewModel = mock<FooterActionsViewModel>()
private val footerActionsViewModelFactory =
mock<FooterActionsViewModel.Factory> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9d3f0d6..006f429 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@
import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -130,7 +130,7 @@
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
- private val communalInteractor by lazy { kosmos.communalInteractor }
+ private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
private val transitionState by lazy {
MutableStateFlow<ObservableTransitionState>(
@@ -155,7 +155,7 @@
LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- communalInteractor = communalInteractor,
+ communalSettingsInteractor = communalSettingsInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 5ef095f..f1f5dc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -82,7 +82,7 @@
scope = testScope.backgroundScope,
)
- private val qsFlexiglassAdapter = FakeQSSceneAdapter { mock() }
+ private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index cc4ebd4..c01f1c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -27,7 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.LaunchableView
import com.android.systemui.assist.AssistManager
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -78,7 +78,7 @@
@Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
- @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+ @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock private lateinit var statusBarWindowController: StatusBarWindowController
@Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
@@ -109,7 +109,7 @@
shadeAnimationInteractor,
Lazy { statusBarKeyguardViewManager },
Lazy { notifShadeWindowController },
- activityLaunchAnimator,
+ mActivityTransitionAnimator,
context,
DISPLAY_ID,
lockScreenUserManager,
@@ -149,7 +149,7 @@
override fun setShouldBlockVisibilityChanges(block: Boolean) {}
}
parent.addView(view)
- val controller = ActivityLaunchAnimator.Controller.fromView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
whenever(pendingIntent.isActivity).thenReturn(true)
whenever(keyguardStateController.isShowing).thenReturn(true)
whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
@@ -163,7 +163,7 @@
)
mainExecutor.runAllReady()
- verify(activityLaunchAnimator)
+ verify(mActivityTransitionAnimator)
.startPendingIntentWithAnimation(
nullable(),
eq(true),
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 6434209..1126ec3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -20,7 +20,7 @@
import android.os.UserHandle;
import android.view.View;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
@@ -54,7 +54,7 @@
*/
void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback,
- @Nullable ActivityLaunchAnimator.Controller animationController);
+ @Nullable ActivityTransitionAnimator.Controller animationController);
/**
* Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching
@@ -64,7 +64,7 @@
*/
void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
@Nullable Runnable intentSentUiThreadCallback,
- @Nullable ActivityLaunchAnimator.Controller animationController);
+ @Nullable ActivityTransitionAnimator.Controller animationController);
/**
* The intent flag can be specified in startActivity().
@@ -72,26 +72,26 @@
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
void startActivity(Intent intent, boolean dismissShade);
default void startActivity(Intent intent, boolean dismissShade,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
+ @Nullable ActivityTransitionAnimator.Controller animationController) {
startActivity(intent, dismissShade, animationController,
false /* showOverLockscreenWhenLocked */);
}
void startActivity(Intent intent, boolean dismissShade,
- @Nullable ActivityLaunchAnimator.Controller animationController,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
boolean showOverLockscreenWhenLocked);
void startActivity(Intent intent, boolean dismissShade,
- @Nullable ActivityLaunchAnimator.Controller animationController,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
boolean showOverLockscreenWhenLocked, UserHandle userHandle);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void postStartActivityDismissingKeyguard(Intent intent, int delay);
void postStartActivityDismissingKeyguard(Intent intent, int delay,
- @Nullable ActivityLaunchAnimator.Controller animationController);
+ @Nullable ActivityTransitionAnimator.Controller animationController);
/** Posts a start activity intent that dismisses keyguard. */
void postStartActivityDismissingKeyguard(Intent intent, int delay,
- @Nullable ActivityLaunchAnimator.Controller animationController,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable String customMessage);
void postStartActivityDismissingKeyguard(PendingIntent intent);
@@ -100,7 +100,7 @@
* animation controller that should be used for the activity launch animation.
*/
void postStartActivityDismissingKeyguard(PendingIntent intent,
- @Nullable ActivityLaunchAnimator.Controller animationController);
+ @Nullable ActivityTransitionAnimator.Controller animationController);
void postQSRunnableDismissingKeyguard(Runnable runnable);
@@ -123,7 +123,7 @@
boolean disallowEnterPictureInPictureWhileLaunching,
Callback callback,
int flags,
- @Nullable ActivityLaunchAnimator.Controller animationController,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
UserHandle userHandle);
/** Execute a runnable after dismissing keyguard. */
diff --git a/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml b/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
index dec9930..a80d3b4 100644
--- a/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_satellite_not_connected.xml
@@ -20,6 +20,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
+ android:alpha="0.3"
>
<path
android:pathData="M14.73,3.36L17.63,6.2C17.83,6.39 17.83,6.71 17.63,6.91L16.89,7.65C16.69,7.85 16.37,7.85 16.18,7.65L13.34,4.78C13.15,4.59 13.15,4.28 13.34,4.08L14.01,3.37C14.2,3.17 14.52,3.16 14.72,3.36H14.73ZM14.37,1C13.85,1 13.32,1.2 12.93,1.61L11.56,3.06C10.8,3.84 10.81,5.09 11.58,5.86L15.13,9.41C15.52,9.8 16.03,10 16.55,10C17.07,10 17.58,9.8 17.97,9.41L19.42,7.96C20.21,7.17 20.2,5.89 19.4,5.12L15.77,1.57C15.38,1.19 14.88,1 14.37,1Z"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1b71256..15688c5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1599,6 +1599,15 @@
<!-- Accessibility label for hotspot icon [CHAR LIMIT=NONE] -->
<string name="accessibility_status_bar_hotspot">Hotspot</string>
+ <!-- Accessibility label for no satellite connection [CHAR LIMIT=NONE] -->
+ <string name="accessibility_status_bar_satellite_no_connection">Satellite, no connection</string>
+ <!-- Accessibility label for poor satellite connection [CHAR LIMIT=NONE] -->
+ <string name="accessibility_status_bar_satellite_poor_connection">Satellite, poor connection</string>
+ <!-- Accessibility label for good satellite connection [CHAR LIMIT=NONE] -->
+ <string name="accessibility_status_bar_satellite_good_connection">Satellite, good connection</string>
+ <!-- Accessibility label for available satellite connection [CHAR LIMIT=NONE] -->
+ <string name="accessibility_status_bar_satellite_available">Satellite, connection available</string>
+
<!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
<string name="accessibility_managed_profile">Work profile</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 1a10c7a..458a21c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -38,7 +38,6 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyboard.data.repository.KeyboardRepository;
import com.android.systemui.log.BouncerLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -212,7 +211,6 @@
private final FeatureFlags mFeatureFlags;
private final SelectedUserInteractor mSelectedUserInteractor;
private final UiEventLogger mUiEventLogger;
- private final KeyboardRepository mKeyboardRepository;
private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
@Inject
@@ -228,7 +226,6 @@
KeyguardViewController keyguardViewController,
FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
UiEventLogger uiEventLogger,
- KeyboardRepository keyboardRepository,
KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -246,7 +243,6 @@
mFeatureFlags = featureFlags;
mSelectedUserInteractor = selectedUserInteractor;
mUiEventLogger = uiEventLogger;
- mKeyboardRepository = keyboardRepository;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
}
@@ -277,7 +273,7 @@
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mUiEventLogger, mKeyboardRepository
+ mUiEventLogger, mKeyguardKeyboardInteractor
);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
@@ -285,14 +281,15 @@
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyboardRepository);
+ mKeyguardKeyboardInteractor);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyboardRepository);
+ mKeyguardKeyboardInteractor
+ );
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 60dd568..476497d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,8 +16,8 @@
package com.android.keyguard;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
@@ -32,9 +32,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyboard.data.repository.KeyboardRepository;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -43,7 +43,7 @@
private final LiftToActivateListener mLiftToActivateListener;
private final FalsingCollector mFalsingCollector;
- private final KeyboardRepository mKeyboardRepository;
+ private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
protected PasswordTextView mPasswordEntry;
private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
@@ -75,13 +75,13 @@
FalsingCollector falsingCollector,
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyboardRepository keyboardRepository) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
emergencyButtonController, featureFlags, selectedUserInteractor);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
- mKeyboardRepository = keyboardRepository;
+ mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
}
@@ -132,7 +132,7 @@
okButton.setOnHoverListener(mLiftToActivateListener);
}
if (pinInputFieldStyledFocusState()) {
- collectFlow(mPasswordEntry, mKeyboardRepository.isAnyKeyboardConnected(),
+ collectFlow(mPasswordEntry, mKeyguardKeyboardInteractor.isAnyKeyboardConnected(),
this::setKeyboardBasedFocusOutline);
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index b958f55..f4cda02 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -25,10 +25,10 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyboard.data.repository.KeyboardRepository;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -61,11 +61,11 @@
FalsingCollector falsingCollector,
DevicePostureController postureController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
- KeyboardRepository keyboardRepository) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyboardRepository);
+ keyguardKeyboardInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 1cdcbd0..558679e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -42,9 +42,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyboard.data.repository.KeyboardRepository;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -94,11 +94,12 @@
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
- SelectedUserInteractor selectedUserInteractor, KeyboardRepository keyboardRepository) {
+ SelectedUserInteractor selectedUserInteractor,
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyboardRepository);
+ keyguardKeyboardInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index f019d61..cb1c4b3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -37,9 +37,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyboard.data.repository.KeyboardRepository;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -91,11 +91,12 @@
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
- SelectedUserInteractor selectedUserInteractor, KeyboardRepository keyboardRepository) {
+ SelectedUserInteractor selectedUserInteractor,
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyboardRepository);
+ keyguardKeyboardInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 4c9782c..39e1c41 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -36,7 +36,7 @@
* If your CoreStartable depends on different CoreStartables starting before it, use a
* {@link com.android.systemui.startable.Dependencies} annotation to list out those dependencies.
*
- * @see SystemUIApplication#startServicesIfNeeded()
+ * @see SystemUIApplication#startSystemUserServicesIfNeeded()
*/
public interface CoreStartable extends Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
index e88aaf01..aab0b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -22,7 +22,6 @@
import android.content.ContentProvider
import android.content.Context
import android.content.Intent
-import android.util.Log
import androidx.core.app.AppComponentFactory
import com.android.systemui.dagger.ContextComponentHelper
import com.android.systemui.dagger.SysUIComponent
@@ -91,7 +90,8 @@
return app
}
- @UsesReflection(KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject"))
+ @UsesReflection(
+ KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject"))
override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
val contentProvider = super.instantiateProviderCompat(cl, className)
if (contentProvider is ContextInitializer) {
@@ -103,11 +103,12 @@
.getMethod("inject", contentProvider.javaClass)
injectMethod.invoke(rootComponent, contentProvider)
} catch (e: NoSuchMethodException) {
- Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ throw RuntimeException("No injector for class: " + contentProvider.javaClass, e)
} catch (e: IllegalAccessException) {
- Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ throw RuntimeException("Injector inaccessible for class: " +
+ contentProvider.javaClass, e)
} catch (e: InvocationTargetException) {
- Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ throw RuntimeException("Error while injecting: " + contentProvider.javaClass, e)
}
initializer
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 8aae206..15ef61e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,6 +42,7 @@
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.res.R;
import com.android.systemui.startable.Dependencies;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -77,6 +78,7 @@
private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback;
private SysUIComponent mSysUIComponent;
private SystemUIInitializer mInitializer;
+ private ProcessWrapper mProcessWrapper;
public SystemUIApplication() {
super();
@@ -115,6 +117,7 @@
// Enable Looper trace points.
// This allows us to see Handler callbacks on traces.
rootComponent.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
+ mProcessWrapper = rootComponent.getProcessWrapper();
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
@@ -132,7 +135,7 @@
View.setTraceLayoutSteps(true);
}
- if (rootComponent.getProcessWrapper().isSystemUser()) {
+ if (mProcessWrapper.isSystemUser()) {
IntentFilter bootCompletedFilter = new
IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -199,7 +202,11 @@
* <p>This method must only be called from the main thread.</p>
*/
- public void startServicesIfNeeded() {
+ public void startSystemUserServicesIfNeeded() {
+ if (!mProcessWrapper.isSystemUser()) {
+ Log.wtf(TAG, "Tried starting SystemUser services on non-SystemUser");
+ return; // Per-user startables are handled in #startSystemUserServicesIfNeeded.
+ }
final String vendorComponent = mInitializer.getVendorComponent(getResources());
// Sort the startables so that we get a deterministic ordering.
@@ -219,6 +226,9 @@
* <p>This method must only be called from the main thread.</p>
*/
void startSecondaryUserServicesIfNeeded() {
+ if (mProcessWrapper.isSystemUser()) {
+ return; // Per-user startables are handled in #startSystemUserServicesIfNeeded.
+ }
// Sort the startables so that we get a deterministic ordering.
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 872b005..1a9b01f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -22,10 +22,10 @@
import android.os.HandlerThread;
import android.util.Log;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.res.R;
import com.android.systemui.util.InitializationChecker;
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
import com.android.wm.shell.keyguard.KeyguardTransitions;
@@ -124,9 +124,6 @@
.setDesktopMode(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
- if (initializeComponents) {
- mSysUIComponent.init();
- }
// Every other part of our codebase currently relies on Dependency, so we
// really need to ensure the Dependency gets initialized early on.
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
index f4ec6f7..407f764 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
@@ -19,12 +19,31 @@
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import android.util.Log;
+
+import com.android.systemui.process.ProcessWrapper;
+
+import javax.inject.Inject;
public class SystemUISecondaryUserService extends Service {
+ private static final String TAG = "SysUISecondaryService";
+
+ private final ProcessWrapper mProcessWrapper;
+
+ @Inject
+ SystemUISecondaryUserService(ProcessWrapper processWrapper) {
+ mProcessWrapper = processWrapper;
+ }
+
@Override
public void onCreate() {
super.onCreate();
+ if (mProcessWrapper.isSystemUser()) {
+ Log.w(TAG, "SecondaryServices started for System User. Stopping it.");
+ stopSelf();
+ return;
+ }
((SystemUIApplication) getApplication()).startSecondaryUserServicesIfNeeded();
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 76c2282..b26be0c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -77,7 +77,7 @@
super.onCreate();
// Start all of SystemUI
- ((SystemUIApplication) getApplication()).startServicesIfNeeded();
+ ((SystemUIApplication) getApplication()).startSystemUserServicesIfNeeded();
// Finish initializing dump logic
mLogBufferFreezer.attach(mBroadcastDispatcher);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
index bf121fb..e57323b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
@@ -26,6 +26,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.text.Layout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -162,6 +163,7 @@
mTextView.setPadding(/* left= */ 0, textPadding, /* right= */ 0, textPadding);
mTextView.setTextSize(COMPLEX_UNIT_PX, textSize);
mTextView.setTextColor(textColor);
+ mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
final ColorStateList colorAccent = Utils.getColorAccent(getContext());
mUndoButton.setText(res.getString(R.string.accessibility_floating_button_undo));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 66fe4b3..716209d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -66,7 +66,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -162,7 +162,7 @@
mUnlockedScreenOffAnimationController;
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
- @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ @NonNull private final ActivityTransitionAnimator mActivityTransitionAnimator;
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@NonNull private final ShadeInteractor mShadeInteractor;
@Nullable private final TouchProcessor mTouchProcessor;
@@ -287,7 +287,7 @@
event,
fromUdfpsView
),
- mActivityLaunchAnimator,
+ mActivityTransitionAnimator,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
@@ -663,7 +663,7 @@
@NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@NonNull SystemUIDialogManager dialogManager,
@NonNull LatencyTracker latencyTracker,
- @NonNull ActivityLaunchAnimator activityLaunchAnimator,
+ @NonNull ActivityTransitionAnimator activityTransitionAnimator,
@NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
@NonNull ShadeInteractor shadeInteractor,
@@ -706,7 +706,7 @@
mSystemClock = systemClock;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLatencyTracker = latencyTracker;
- mActivityLaunchAnimator = activityLaunchAnimator;
+ mActivityTransitionAnimator = activityTransitionAnimator;
mSensorProps = new FingerprintSensorPropertiesInternal(
-1 /* sensorId */,
SensorProperties.STRENGTH_CONVENIENCE,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 4ea5f4c..a209eae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,7 +44,7 @@
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.binder.UdfpsTouchOverlayBinder
@@ -100,7 +100,7 @@
@RequestReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
@@ -304,7 +304,7 @@
unlockedScreenOffAnimationController,
dialogManager,
controller,
- activityLaunchAnimator,
+ activityTransitionAnimator,
primaryBouncerInteractor,
alternateBouncerInteractor,
udfpsKeyguardAccessibilityDelegate,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 7020d05..018d92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -25,7 +25,7 @@
import com.android.app.animation.Interpolators
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -69,7 +69,7 @@
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
systemUIDialogManager: SystemUIDialogManager,
private val udfpsController: UdfpsController,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
@@ -137,20 +137,20 @@
}
}
- private val activityLaunchAnimatorListener: ActivityLaunchAnimator.Listener =
- object : ActivityLaunchAnimator.Listener {
- override fun onLaunchAnimationStart() {
+ private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener =
+ object : ActivityTransitionAnimator.Listener {
+ override fun onTransitionAnimationStart() {
isLaunchingActivity = true
activityLaunchProgress = 0f
updateAlpha()
}
- override fun onLaunchAnimationEnd() {
+ override fun onTransitionAnimationEnd() {
isLaunchingActivity = false
updateAlpha()
}
- override fun onLaunchAnimationProgress(linearProgress: Float) {
+ override fun onTransitionAnimationProgress(linearProgress: Float) {
activityLaunchProgress = linearProgress
updateAlpha()
}
@@ -370,7 +370,7 @@
updatePauseAuth()
keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
- activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
+ activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener)
view.startIconAsyncInflate {
val animationViewInternal: View =
view.requireViewById(R.id.udfps_animation_view_internal)
@@ -389,7 +389,7 @@
if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
}
- activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
+ activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener)
keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
}
@@ -536,7 +536,7 @@
val udfpsActivityLaunchAlphaMultiplier =
1f -
(activityLaunchProgress *
- (ActivityLaunchAnimator.TIMINGS.totalDuration / 83))
+ (ActivityTransitionAnimator.TIMINGS.totalDuration / 83))
.coerceIn(0f, 1f)
alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 12be32c..964eb6f 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -24,17 +24,12 @@
import androidx.annotation.LayoutRes
import com.android.settingslib.Utils
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
import com.android.systemui.statusbar.policy.onThemeChanged
import com.android.systemui.util.kotlin.emitOnStart
-import com.android.systemui.util.view.bindLatest
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -91,46 +86,3 @@
.map { layoutInflater.inflate(id, root, attachToRoot) as T }
}
}
-
-/**
- * Perform an inflation right away, then re-inflate whenever the device configuration changes, and
- * call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by
- * [onInflate] when done.
- *
- * This never completes unless cancelled, it just suspends and waits for updates. It runs on a
- * background thread using [backgroundDispatcher].
- *
- * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
- *
- * An example use-case of this is when a view needs to be re-inflated whenever a configuration
- * change occurs, which would require the ViewBinder to then re-bind the new view. For example, the
- * code in the parent view's binder would look like:
- * ```
- * parentView.repeatWhenAttached {
- * configurationState
- * .reinflateAndBindLatest(
- * R.layout.my_layout,
- * parentView,
- * attachToRoot = false,
- * coroutineScope = lifecycleScope,
- * configurationController.onThemeChanged,
- * ) { view: ChildView ->
- * ChildViewBinder.bind(view, childViewModel)
- * }
- * }
- * ```
- *
- * In turn, the bind method (passed through [onInflate]) uses [repeatWhenAttached], which returns a
- * [DisposableHandle].
- */
-suspend fun <T : View> ConfigurationState.reinflateAndBindLatest(
- @LayoutRes resource: Int,
- root: ViewGroup?,
- attachToRoot: Boolean,
- backgroundDispatcher: CoroutineDispatcher,
- onInflate: (T) -> DisposableHandle?,
-) {
- inflateLayout<T>(resource, root, attachToRoot)
- .flowOn(backgroundDispatcher)
- .bindLatest(onInflate)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index dc07c1b..0bad33b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalRepositoryModule
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -36,6 +37,7 @@
CommunalWidgetRepositoryModule::class,
CommunalDatabaseModule::class,
CommunalPrefsRepositoryModule::class,
+ CommunalSettingsRepositoryModule::class,
]
)
interface CommunalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
new file mode 100644
index 0000000..83a5bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.data.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+import java.util.EnumSet
+
+/** Reasons that communal is disabled, primarily for logging. */
+enum class DisabledReason(val loggingString: String) {
+ /** Communal should be disabled due to invalid current user */
+ DISABLED_REASON_INVALID_USER("invalidUser"),
+ /** Communal should be disabled due to the flag being off */
+ DISABLED_REASON_FLAG("flag"),
+ /** Communal should be disabled because the user has turned off the setting */
+ DISABLED_REASON_USER_SETTING("userSetting"),
+ /** Communal is disabled by the device policy app */
+ DISABLED_REASON_DEVICE_POLICY("devicePolicy"),
+}
+
+/**
+ * Model representing the reasons communal hub should be disabled. Allows logging reasons separately
+ * for debugging.
+ */
+@JvmInline
+value class CommunalEnabledState(
+ private val disabledReasons: EnumSet<DisabledReason> =
+ EnumSet.noneOf(DisabledReason::class.java)
+) : Diffable<CommunalEnabledState>, Set<DisabledReason> by disabledReasons {
+
+ /** Creates [CommunalEnabledState] with a single reason for being disabled */
+ constructor(reason: DisabledReason) : this(EnumSet.of(reason))
+
+ /** Checks if there are any reasons communal should be disabled. If none, returns true. */
+ val enabled: Boolean
+ get() = isEmpty()
+
+ override fun logDiffs(prevVal: CommunalEnabledState, row: TableRowLogger) {
+ for (reason in DisabledReason.entries) {
+ val newVal = contains(reason)
+ if (newVal != prevVal.contains(reason)) {
+ row.logChange(
+ columnName = reason.loggingString,
+ value = newVal,
+ )
+ }
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ for (reason in DisabledReason.entries) {
+ row.logChange(columnName = reason.loggingString, value = contains(reason))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index addd880..4a06585f5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -16,22 +16,14 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.Flags.communalHub
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.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -39,26 +31,13 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
- /** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean
-
- /**
- * A {@link StateFlow} that tracks whether communal hub is enabled (it can be disabled in
- * settings).
- */
- val communalEnabledState: StateFlow<Boolean>
-
/** Whether the communal hub is showing. */
val isCommunalHubShowing: Flow<Boolean>
@@ -87,37 +66,11 @@
class CommunalRepositoryImpl
@Inject
constructor(
- @Application private val applicationScope: CoroutineScope,
@Background backgroundScope: CoroutineScope,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
- private val featureFlagsClassic: FeatureFlagsClassic,
sceneContainerFlags: SceneContainerFlags,
sceneContainerRepository: SceneContainerRepository,
- userRepository: UserRepository,
- private val secureSettings: SecureSettings
) : CommunalRepository {
- private val communalEnabledSettingState: Flow<Boolean> =
- userRepository.selectedUserInfo
- .flatMapLatest { userInfo -> observeSettings(userInfo.id) }
- .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed())
-
- override val communalEnabledState: StateFlow<Boolean> =
- if (featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()) {
- communalEnabledSettingState
- .filterNotNull()
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = true
- )
- } else {
- MutableStateFlow(false)
- }
-
- override val isCommunalEnabled: Boolean
- get() = communalEnabledState.value
-
private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
MutableStateFlow(CommunalSceneKey.DEFAULT)
override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
@@ -153,26 +106,4 @@
} else {
desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
}
-
- private fun observeSettings(userId: Int): Flow<Boolean> =
- secureSettings
- .observerFlow(
- userId = userId,
- names =
- arrayOf(
- GLANCEABLE_HUB_ENABLED,
- )
- )
- // Force an update
- .onStart { emit(Unit) }
- .map { readFromSettings(userId) }
-
- private suspend fun readFromSettings(userId: Int): Boolean =
- withContext(backgroundDispatcher) {
- secureSettings.getIntForUser(GLANCEABLE_HUB_ENABLED, 1, userId) == 1
- }
-
- companion object {
- private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
new file mode 100644
index 0000000..201b049
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import com.android.systemui.Flags.communalHub
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import java.util.EnumSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+interface CommunalSettingsRepository {
+ /** A [CommunalEnabledState] for the specified user. */
+ fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+}
+
+@SysUISingleton
+class CommunalSettingsRepositoryImpl
+@Inject
+constructor(
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val featureFlagsClassic: FeatureFlagsClassic,
+ private val secureSettings: SecureSettings,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val devicePolicyManager: DevicePolicyManager,
+) : CommunalSettingsRepository {
+
+ private val flagEnabled: Boolean by lazy {
+ featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
+ }
+
+ override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
+ if (!user.isMain) {
+ return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER))
+ }
+ if (!flagEnabled) {
+ return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG))
+ }
+ return combine(
+ getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING),
+ getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY),
+ ) { reasons ->
+ reasons.filterNotNull()
+ }
+ .map { reasons ->
+ if (reasons.isEmpty()) {
+ EnumSet.noneOf(DisabledReason::class.java)
+ } else {
+ EnumSet.copyOf(reasons)
+ }
+ }
+ .map { reasons -> CommunalEnabledState(reasons) }
+ .flowOn(bgDispatcher)
+ }
+
+ private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ secureSettings.getIntForUser(
+ GLANCEABLE_HUB_ENABLED,
+ ENABLED_SETTING_DEFAULT,
+ user.id,
+ ) == 1
+ }
+
+ private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ user = user.userHandle
+ )
+ .emitOnStart()
+ .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+
+ companion object {
+ const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+ private const val ENABLED_SETTING_DEFAULT = 1
+ }
+}
+
+private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
+ (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
+
+private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled ->
+ if (enabled) null else reason
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
index 128f58b..a931d3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,8 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.animation
+package com.android.systemui.communal.data.repository
-import com.android.systemui.kosmos.Kosmos
+import dagger.Binds
+import dagger.Module
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+@Module
+interface CommunalSettingsRepositoryModule {
+ @Binds
+ fun communalSettingsRepository(impl: CommunalSettingsRepositoryImpl): CommunalSettingsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 950ac3c..23f590e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -42,7 +42,6 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
-import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
@@ -74,8 +73,8 @@
private val communalPrefsRepository: CommunalPrefsRepository,
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
- userRepository: UserRepository,
keyguardInteractor: KeyguardInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
@CommunalLog logBuffer: LogBuffer,
@@ -90,13 +89,12 @@
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
- get() = communalRepository.isCommunalEnabled
+ get() = communalSettingsInteractor.isCommunalEnabled.value
/** Whether communal features are enabled and available. */
val isCommunalAvailable: Flow<Boolean> =
and(
- communalRepository.communalEnabledState,
- userRepository.selectedUserInfo.map { it.isMain },
+ communalSettingsInteractor.isCommunalEnabled,
not(keyguardInteractor.isEncryptedOrLockdown),
or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming)
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
new file mode 100644
index 0000000..0b096ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalSettingsInteractor
+@Inject
+constructor(
+ @Background private val bgScope: CoroutineScope,
+ private val repository: CommunalSettingsRepository,
+ userInteractor: SelectedUserInteractor,
+ @CommunalTableLog tableLogBuffer: TableLogBuffer,
+) {
+ /** Whether or not communal is enabled for the currently selected user. */
+ val isCommunalEnabled: StateFlow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.getEnabledState(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "disabledReason",
+ initialValue = CommunalEnabledState()
+ )
+ .map { model -> model.enabled }
+ // Start this eagerly since the value is accessed synchronously in many places.
+ .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 1404ee2..25dfc02 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -28,17 +28,18 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.launch
/** Encapsulates business-logic related to communal tutorial state. */
@@ -51,6 +52,7 @@
private val communalTutorialRepository: CommunalTutorialRepository,
keyguardInteractor: KeyguardInteractor,
private val communalRepository: CommunalRepository,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
communalInteractor: CommunalInteractor,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) {
@@ -110,20 +112,24 @@
return null
}
- private var job: Job? = null
private fun listenForTransitionToUpdateTutorialState() {
- if (!communalRepository.isCommunalEnabled) {
- return
- }
- job =
- scope.launch {
- tutorialStateToUpdate.collect {
- communalTutorialRepository.setTutorialState(it)
- if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
- job?.cancel()
+ scope.launch {
+ communalSettingsInteractor.isCommunalEnabled
+ .flatMapLatest { enabled ->
+ if (!enabled) {
+ emptyFlow()
+ } else {
+ tutorialStateToUpdate
}
}
- }
+ .transformWhile { tutorialState ->
+ emit(tutorialState)
+ tutorialState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+ }
+ .collect { tutorialState ->
+ communalTutorialRepository.setTutorialState(tutorialState)
+ }
+ }
}
init {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index afa7fa9..4c1e77b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -19,7 +19,7 @@
import android.app.PendingIntent
import android.view.View
import android.widget.RemoteViews
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.ui.view.getNearestParent
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
@@ -42,7 +42,7 @@
private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean {
val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
- val animationController = hostView?.let(ActivityLaunchAnimator.Controller::fromView)
+ val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
activityStarter.startPendingIntentMaybeDismissingKeyguard(
pendingIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index 9d4ed20..afa2375 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -35,7 +35,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent;
@@ -275,8 +275,9 @@
.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
.putExtra(ControlsUiController.EXIT_TO_DREAM, true);
- final ActivityLaunchAnimator.Controller controller =
- v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */)
+ final ActivityTransitionAnimator.Controller controller =
+ v != null
+ ? ActivityTransitionAnimator.Controller.fromView(v, null /* cujType */)
: null;
if (mControlsComponent.getVisibility() == AVAILABLE) {
// Controls can be made visible.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index dd186d6..f7bc5cdc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -26,10 +26,13 @@
import com.android.systemui.ScreenDecorationsModule;
import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.battery.BatterySaverModule;
+import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.media.dagger.MediaModule;
+import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.navigationbar.NavigationBarControllerModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
@@ -63,6 +66,7 @@
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
import com.android.systemui.toast.ToastModule;
+import com.android.systemui.unfold.SysUIUnfoldStartableModule;
import com.android.systemui.unfold.UnfoldTransitionModule;
import com.android.systemui.volume.dagger.VolumeModule;
import com.android.systemui.wallpapers.dagger.WallpaperModule;
@@ -92,12 +96,15 @@
AospPolicyModule.class,
BatterySaverModule.class,
CollapsedStatusBarFragmentStartableModule.class,
+ ConnectingDisplayViewModel.StartableModule.class,
GestureModule.class,
HeadsUpModule.class,
KeyboardShortcutsModule.class,
MediaModule.class,
+ MediaMuteAwaitConnectionCli.StartableModule.class,
MultiUserUtilsModule.class,
NavigationBarControllerModule.class,
+ NearbyMediaDevicesManager.StartableModule.class,
PowerModule.class,
QSModule.class,
RearDisplayModule.class,
@@ -108,6 +115,7 @@
ShadeModule.class,
StartCentralSurfacesModule.class,
SceneContainerFrameworkModule.class,
+ SysUIUnfoldStartableModule.class,
UnfoldTransitionModule.Startables.class,
ToastModule.class,
VolumeModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 7bf67d7..3b0c281 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -19,25 +19,15 @@
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
-import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.PerUser;
-import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
-import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.unfold.FoldStateLogger;
-import com.android.systemui.unfold.FoldStateLoggingProvider;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-import com.android.systemui.unfold.dagger.UnfoldBg;
-import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.desktopmode.DesktopMode;
@@ -126,42 +116,6 @@
}
/**
- * Initializes all the SysUI components.
- */
- default void init() {
- // Initialize components that have no direct tie to the dagger dependency graph,
- // but are critical to this component's operation
- getSysUIUnfoldComponent()
- .ifPresent(
- c -> {
- c.getFullScreenLightRevealAnimations().forEach(it -> it.init());
- c.getUnfoldTransitionWallpaperController().init();
- c.getUnfoldHapticsPlayer();
- c.getNaturalRotationUnfoldProgressProvider().init();
- c.getUnfoldLatencyTracker().init();
- });
- // No init method needed, just needs to be gotten so that it's created.
- getMediaMuteAwaitConnectionCli();
- getNearbyMediaDevicesManager();
- getConnectingDisplayViewModel().init();
- getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
- getFoldStateLogger().ifPresent(FoldStateLogger::init);
-
- Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider;
-
- if (Flags.unfoldAnimationBackgroundProgress()) {
- unfoldTransitionProgressProvider = getBgUnfoldTransitionProgressProvider();
- } else {
- unfoldTransitionProgressProvider = getUnfoldTransitionProgressProvider();
- }
- unfoldTransitionProgressProvider
- .ifPresent(
- (progressProvider) ->
- getUnfoldTransitionProgressForwarder()
- .ifPresent(progressProvider::addCallback));
- }
-
- /**
* Provides a BootCompleteCache.
*/
@SysUISingleton
@@ -180,37 +134,6 @@
ContextComponentHelper getContextComponentHelper();
/**
- * Creates a UnfoldTransitionProgressProvider that calculates progress in the background.
- */
- @SysUISingleton
- @UnfoldBg
- Optional<UnfoldTransitionProgressProvider> getBgUnfoldTransitionProgressProvider();
-
- /**
- * Creates a UnfoldTransitionProgressProvider that calculates progress in the main thread.
- */
- @SysUISingleton
- Optional<UnfoldTransitionProgressProvider> getUnfoldTransitionProgressProvider();
-
- /**
- * Creates a UnfoldTransitionProgressForwarder.
- */
- @SysUISingleton
- Optional<UnfoldTransitionProgressForwarder> getUnfoldTransitionProgressForwarder();
-
- /**
- * Creates a FoldStateLoggingProvider.
- */
- @SysUISingleton
- Optional<FoldStateLoggingProvider> getFoldStateLoggingProvider();
-
- /**
- * Creates a FoldStateLogger.
- */
- @SysUISingleton
- Optional<FoldStateLogger> getFoldStateLogger();
-
- /**
* Main dependency providing module.
*/
@SysUISingleton
@@ -227,22 +150,6 @@
InitController getInitController();
/**
- * For devices with a hinge: access objects within this component
- */
- Optional<SysUIUnfoldComponent> getSysUIUnfoldComponent();
-
- /** */
- MediaMuteAwaitConnectionCli getMediaMuteAwaitConnectionCli();
-
- /** */
- NearbyMediaDevicesManager getNearbyMediaDevicesManager();
-
- /**
- * Creates a ConnectingDisplayViewModel
- */
- ConnectingDisplayViewModel getConnectingDisplayViewModel();
-
- /**
* Returns {@link CoreStartable}s that should be started with the application.
*/
Map<Class<?>, Provider<CoreStartable>> getStartables();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 28fd9a9..1720de8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.dagger;
import android.app.INotificationManager;
+import android.app.Service;
import android.content.Context;
import android.service.dreams.IDreamManager;
@@ -28,6 +29,7 @@
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CameraProtectionModule;
+import com.android.systemui.SystemUISecondaryUserService;
import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.appops.dagger.AppOpsModule;
@@ -150,6 +152,8 @@
import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
import java.util.Collections;
import java.util.Optional;
@@ -384,4 +388,9 @@
@Binds
abstract LargeScreenShadeInterpolator largeScreensShadeInterpolator(
LargeScreenShadeInterpolatorImpl impl);
+
+ @Binds
+ @IntoMap
+ @ClassKey(SystemUISecondaryUserService.class)
+ abstract Service bindsSystemUISecondaryUserService(SystemUISecondaryUserService service);
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index 684627b..2461c26 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -39,4 +39,6 @@
/** Provide the current status of fingerprint authentication. */
val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
repository.authenticationStatus
+
+ val isLockedOut: Flow<Boolean> = repository.isLockedOut
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 98130eb..cf91e14 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -36,7 +36,6 @@
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
@@ -78,7 +77,7 @@
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val faceAuthenticationLogger: FaceAuthenticationLogger,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
private val userRepository: UserRepository,
private val facePropertyRepository: FacePropertyRepository,
private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
@@ -149,14 +148,24 @@
}
.launchIn(applicationScope)
- deviceEntryFingerprintAuthRepository.isLockedOut
- .onEach {
- if (it) {
+ deviceEntryFingerprintAuthInteractor.isLockedOut
+ .sample(biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, ::Pair)
+ .filter { (_, faceEnabledAndEnrolled) ->
+ // We don't care about this if face auth is not enabled.
+ faceEnabledAndEnrolled
+ }
+ .map { (fpLockedOut, _) -> fpLockedOut }
+ .sample(userRepository.selectedUser, ::Pair)
+ .onEach { (fpLockedOut, currentUser) ->
+ if (fpLockedOut) {
faceAuthenticationLogger.faceLockedOut("Fingerprint locked out")
- // We don't care about this if face auth is not enabled.
if (isFaceAuthEnabledAndEnrolled()) {
repository.setLockedOut(true)
}
+ } else {
+ // Fingerprint is not locked out anymore, revert face lockout state back to
+ // previous value.
+ resetLockedOutState(currentUser.userInfo.id)
}
}
.launchIn(applicationScope)
@@ -169,10 +178,7 @@
val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
if (wasSwitching && !isSwitching) {
- val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
- repository.setLockedOut(
- lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
- )
+ resetLockedOutState(curr.userInfo.id)
yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
@@ -185,6 +191,13 @@
.launchIn(applicationScope)
}
+ private suspend fun resetLockedOutState(currentUserId: Int) {
+ val lockoutMode = facePropertyRepository.getLockoutMode(currentUserId)
+ repository.setLockedOut(
+ lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED
+ )
+ }
+
override fun onSwipeUpOnBouncer() {
runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 10aa703..190062c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -18,6 +18,7 @@
import android.app.Dialog
import android.content.Context
import com.android.server.policy.feature.flags.Flags
+import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.Utils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -26,6 +27,10 @@
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.ui.view.MirroringConfirmationDialog
import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -47,12 +52,12 @@
@Application private val scope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
private val configurationController: ConfigurationController,
-) {
+) : CoreStartable {
private var dialog: Dialog? = null
/** Starts listening for pending displays. */
- fun init() {
+ override fun start() {
val pendingDisplayFlow = connectedDisplayInteractor.pendingDisplay
val concurrentDisplaysInProgessFlow =
if (Flags.enableDualDisplayBlocking()) {
@@ -96,4 +101,12 @@
dialog?.hide()
dialog = null
}
+
+ @Module
+ interface StartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(ConnectingDisplayViewModel::class)
+ fun bindsConnectingDisplayViewModel(impl: ConnectingDisplayViewModel): CoreStartable
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 4cabd70..2e233d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -327,7 +327,7 @@
@Override
public void onCreate() {
- ((SystemUIApplication) getApplication()).startServicesIfNeeded();
+ ((SystemUIApplication) getApplication()).startSystemUserServicesIfNeeded();
if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) {
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 39bbf07..0ee924d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -130,7 +130,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -957,8 +957,8 @@
* Animation launch controller for activities that occlude the keyguard.
*/
@VisibleForTesting
- final ActivityLaunchAnimator.Controller mOccludeAnimationController =
- new ActivityLaunchAnimator.Controller() {
+ final ActivityTransitionAnimator.Controller mOccludeAnimationController =
+ new ActivityTransitionAnimator.Controller() {
@Override
public void onTransitionAnimationStart(boolean isExpandingFullyAbove) {
mOccludeAnimationPlaying = true;
@@ -966,7 +966,8 @@
}
@Override
- public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
+ public void onTransitionAnimationCancelled(
+ @Nullable Boolean newKeyguardOccludedState) {
Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
+ mOccluded);
mOccludeAnimationPlaying = false;
@@ -1003,7 +1004,7 @@
public void setTransitionContainer(@NonNull ViewGroup transitionContainer) {
// No-op, launch container is always the shade.
Log.wtf(TAG, "Someone tried to change the launch container for the "
- + "ActivityLaunchAnimator, which should never happen.");
+ + "ActivityTransitionAnimator, which should never happen.");
}
@NonNull
@@ -1167,8 +1168,8 @@
/**
* Animation controller for activities that unocclude the keyguard. This does not use the
- * ActivityLaunchAnimator since we're just translating down, rather than emerging from a view
- * or the power button.
+ * ActivityTransitionAnimator since we're just translating down, rather than emerging from a
+ * view or the power button.
*/
private final IRemoteAnimationRunner mUnoccludeAnimationRunner =
new IRemoteAnimationRunner.Stub() {
@@ -1347,7 +1348,7 @@
private boolean mWallpaperSupportsAmbientMode;
private final KeyguardTransitions mKeyguardTransitions;
- private final Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
+ private final Lazy<ActivityTransitionAnimator> mActivityTransitionAnimator;
private final Lazy<ScrimController> mScrimControllerLazy;
private final IActivityTaskManager mActivityTaskManagerService;
@@ -1394,7 +1395,7 @@
WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ Lazy<ActivityTransitionAnimator> activityTransitionAnimator,
Lazy<ScrimController> scrimControllerLazy,
IActivityTaskManager activityTaskManagerService,
FeatureFlags featureFlags,
@@ -1459,7 +1460,7 @@
mJavaAdapter = javaAdapter;
mWallpaperRepository = wallpaperRepository;
- mActivityLaunchAnimator = activityLaunchAnimator;
+ mActivityTransitionAnimator = activityTransitionAnimator;
mScrimControllerLazy = scrimControllerLazy;
mActivityTaskManagerService = activityTaskManagerService;
@@ -3812,15 +3813,15 @@
/**
* Implementation of RemoteAnimationRunner that creates a new
- * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the
+ * {@link ActivityTransitionAnimator.Runner} whenever onAnimationStart is called, delegating the
* remote animation methods to that runner.
*/
private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub {
- private final ActivityLaunchAnimator.Controller mActivityLaunchController;
- @Nullable private ActivityLaunchAnimator.Runner mRunner;
+ private final ActivityTransitionAnimator.Controller mActivityLaunchController;
+ @Nullable private ActivityTransitionAnimator.Runner mRunner;
- ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) {
+ ActivityLaunchRemoteAnimationRunner(ActivityTransitionAnimator.Controller controller) {
mActivityLaunchController = controller;
}
@@ -3837,7 +3838,7 @@
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback)
throws RemoteException {
- mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController);
+ mRunner = mActivityTransitionAnimator.get().createRunner(mActivityLaunchController);
mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
}
}
@@ -3850,7 +3851,7 @@
extends ActivityLaunchRemoteAnimationRunner {
OccludeActivityLaunchRemoteAnimationRunner(
- ActivityLaunchAnimator.Controller controller) {
+ ActivityTransitionAnimator.Controller controller) {
super(controller);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 70da3e7..0b227fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -35,7 +35,7 @@
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
@@ -150,7 +150,7 @@
WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ Lazy<ActivityTransitionAnimator> activityTransitionAnimator,
Lazy<ScrimController> scrimControllerLazy,
IActivityTaskManager activityTaskManagerService,
FeatureFlags featureFlags,
@@ -196,7 +196,7 @@
wallpaperRepository,
shadeController,
notificationShadeWindowController,
- activityLaunchAnimator,
+ activityTransitionAnimator,
scrimControllerLazy,
activityTaskManagerService,
featureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index cc1cf91..7ae70a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -74,6 +74,7 @@
BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale())
}
.distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Lazily, BurnInModel())
/**
* Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 5606d43..e0b5c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -98,6 +98,8 @@
val modeOnCanceled =
if (lastStartedStep.from == KeyguardState.LOCKSCREEN) {
TransitionModeOnCanceled.REVERSE
+ } else if (lastStartedStep.from == KeyguardState.GONE) {
+ TransitionModeOnCanceled.RESET
} else {
TransitionModeOnCanceled.LAST_VALUE
}
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 00b7989..8b278cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -112,6 +112,38 @@
interpolator: Interpolator = LINEAR,
name: String? = null
): Flow<Float> {
+ return sharedFlowWithState(
+ duration = duration,
+ onStep = onStep,
+ startTime = startTime,
+ onStart = onStart,
+ onCancel = onCancel,
+ onFinish = onFinish,
+ interpolator = interpolator,
+ name = name,
+ )
+ .mapNotNull { stateToValue -> stateToValue.value }
+ }
+
+ /**
+ * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
+ * in the range of [0, 1]. View animations should begin and end within a subset of this
+ * range. This function maps the [startTime] and [duration] into [0, 1], when this subset is
+ * valid.
+ *
+ * Will return a [StateToValue], which encompasses the calculated value as well as the
+ * transitionState that is associated with it.
+ */
+ fun sharedFlowWithState(
+ duration: Duration,
+ onStep: (Float) -> Float,
+ startTime: Duration = 0.milliseconds,
+ onStart: (() -> Unit)? = null,
+ onCancel: (() -> Float)? = null,
+ onFinish: (() -> Float)? = null,
+ interpolator: Interpolator = LINEAR,
+ name: String? = null
+ ): Flow<StateToValue> {
if (!duration.isPositive()) {
throw IllegalArgumentException("duration must be a positive number: $duration")
}
@@ -164,7 +196,6 @@
.also { logger.logTransitionStep(name, step, it.value) }
}
.distinctUntilChanged()
- .mapNotNull { stateToValue -> stateToValue.value }
}
/**
@@ -174,9 +205,9 @@
return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
}
}
-
- data class StateToValue(
- val transitionState: TransitionState,
- val value: Float?,
- )
}
+
+data class StateToValue(
+ val transitionState: TransitionState = TransitionState.FINISHED,
+ val value: Float? = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 96e83b0..3630b40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -32,7 +32,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.shared.model.Icon
@@ -534,7 +534,7 @@
activityStarter.postStartActivityDismissingKeyguard(
WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
/* delay= */ 0,
- /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
+ /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
/* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 789d30f..9e7c70d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -145,9 +145,7 @@
launch {
viewModel.burnInLayerVisibility.collect { visibility ->
childViews[burnInLayerId]?.visibility = visibility
- // Reset alpha only for the icons, as they currently have their
- // own animator
- childViews[aodNotificationIconContainerId]?.alpha = 0f
+ childViews[aodNotificationIconContainerId]?.visibility = visibility
}
}
@@ -313,6 +311,12 @@
}
}
+ if (KeyguardShadeMigrationNssl.isEnabled) {
+ burnInParams.update { current ->
+ current.copy(translationY = { childViews[burnInLayerId]?.translationY })
+ }
+ }
+
onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams)
view.addOnLayoutChangeListener(onLayoutChangeListener)
@@ -435,11 +439,17 @@
}
when {
!isVisible.isAnimating -> {
- alpha = 1f
if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = 0f
}
- visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
+ visibility =
+ if (isVisible.value) {
+ alpha = 1f
+ View.VISIBLE
+ } else {
+ alpha = 0f
+ View.INVISIBLE
+ }
}
newAodTransition() -> {
animateInIconTranslation()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index f67cb68..b1adef4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -22,7 +22,7 @@
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -115,7 +115,7 @@
activityStarter.postStartActivityDismissingKeyguard(
WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
/* delay= */ 0,
- /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
+ /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
/* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index 9cf3c95..d4ea728 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
/** Models UI state for the alpha of the AOD (always-on display). */
@SysUISingleton
@@ -42,13 +43,15 @@
/** The alpha level for the entire lockscreen while in AOD. */
val alpha: Flow<Float> =
combine(
- keyguardTransitionInteractor.currentKeyguardState,
+ keyguardTransitionInteractor.transitionValue(KeyguardState.GONE).onStart {
+ emit(0f)
+ },
merge(
keyguardInteractor.keyguardAlpha,
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
)
- ) { currentKeyguardState, alpha ->
- if (currentKeyguardState == KeyguardState.GONE) {
+ ) { transitionToGone, alpha ->
+ if (transitionToGone == 1f) {
// Ensures content is not visible when in GONE state
0f
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 828e033..8110de2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -31,6 +31,10 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import javax.inject.Inject
@@ -58,6 +62,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val keyguardClockViewModel: KeyguardClockViewModel,
) {
@@ -83,21 +88,22 @@
burnIn(params).map { it.translationY.toFloat() }.onStart { emit(0f) },
goneToAodTransitionViewModel
.enterFromTopTranslationY(enterFromTopAmount)
- .onStart { emit(0f) },
+ .onStart { emit(StateToValue()) },
occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
emit(0f)
},
- ) {
- keyguardTransitionY,
- burnInTranslationY,
- goneToAodTransitionTranslationY,
- occludedToLockscreenTransitionTranslationY ->
-
- // All values need to be combined for a smooth translation
- keyguardTransitionY +
- burnInTranslationY +
- goneToAodTransitionTranslationY +
- occludedToLockscreenTransitionTranslationY
+ aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
+ emit(StateToValue())
+ },
+ ) { keyguardTranslationY, burnInY, goneToAod, occludedToLockscreen, aodToLockscreen
+ ->
+ if (isInTransition(aodToLockscreen.transitionState)) {
+ aodToLockscreen.value ?: 0f
+ } else if (isInTransition(goneToAod.transitionState)) {
+ (goneToAod.value ?: 0f) + burnInY
+ } else {
+ burnInY + occludedToLockscreen + keyguardTranslationY
+ }
}
}
.distinctUntilChanged()
@@ -115,6 +121,10 @@
}
}
+ private fun isInTransition(state: TransitionState): Boolean {
+ return state == STARTED || state == RUNNING
+ }
+
private fun burnIn(
params: BurnInParameters,
): Flow<BurnInModel> {
@@ -185,6 +195,8 @@
val topInset: Int = 0,
/** Status view top, without translation added in */
val statusViewTop: Int = 0,
+ /** The current y translation of the view */
+ val translationY: () -> Float? = { null }
)
/**
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 266fd02..6d1d3cb 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
@@ -16,11 +16,14 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
+import com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN
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.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -48,6 +51,22 @@
to = KeyguardState.LOCKSCREEN,
)
+ /**
+ * Begin the transition from wherever the y-translation value is currently. This helps ensure a
+ * smooth transition if a transition in canceled.
+ */
+ fun translationY(currentTranslationY: () -> Float?): Flow<StateToValue> {
+ var startValue = 0f
+ return transitionAnimation.sharedFlowWithState(
+ duration = 500.milliseconds,
+ onStart = {
+ startValue = currentTranslationY() ?: 0f
+ startValue
+ },
+ onStep = { MathUtils.lerp(startValue, 0f, FAST_OUT_SLOW_IN.getInterpolation(it)) },
+ )
+ }
+
/** Ensure alpha is set to be visible */
val lockscreenAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
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 f5e6135..85885b0 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
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -48,8 +49,8 @@
)
/** y-translation from the top of the screen for AOD */
- fun enterFromTopTranslationY(translatePx: Int): Flow<Float> {
- return transitionAnimation.sharedFlow(
+ fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
+ return transitionAnimation.sharedFlowWithState(
startTime = 600.milliseconds,
duration = 500.milliseconds,
onStart = { translatePx },
@@ -63,8 +64,8 @@
/** alpha animation upon entering AOD */
val enterFromTopAnimationAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
- startTime = 600.milliseconds,
- duration = 500.milliseconds,
+ startTime = 700.milliseconds,
+ duration = 400.milliseconds,
onStart = { 0f },
onStep = { it },
onFinish = { 1f },
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index f8a12bd..ec13228 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -30,6 +30,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -48,6 +50,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -78,6 +81,12 @@
val goneToAodTransition = keyguardTransitionInteractor.transition(from = GONE, to = AOD)
+ private val goneToAodTransitionRunning: Flow<Boolean> =
+ goneToAodTransition
+ .map { it.transitionState == STARTED || it.transitionState == RUNNING }
+ .onStart { emit(false) }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -138,6 +147,7 @@
/** Is the notification icon container visible? */
val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
combine(
+ goneToAodTransitionRunning,
keyguardTransitionInteractor.finishedKeyguardState.map {
KeyguardState.lockscreenVisibleInState(it)
},
@@ -145,6 +155,7 @@
areNotifsFullyHiddenAnimated(),
isPulseExpandingAnimated(),
) {
+ goneToAodTransitionRunning: Boolean,
onKeyguard: Boolean,
isBypassEnabled: Boolean,
notifsFullyHidden: AnimatedValue<Boolean>,
@@ -154,7 +165,9 @@
// Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
// animation is playing, in which case we want them to be visible if we're
// animating in the AOD UI and will be switching to KEYGUARD shortly.
- !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() ->
+ goneToAodTransitionRunning ||
+ (!onKeyguard &&
+ !screenOffAnimationController.shouldShowAodIconsWhenShade()) ->
AnimatedValue.NotAnimating(false)
else ->
zip(notifsFullyHidden, pulseExpanding) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 2b28a71..fa18557 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@
constructor(
@Application applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
- communalInteractor: CommunalInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
val longPress: KeyguardLongPressViewModel,
val notifications: NotificationsPlaceholderViewModel,
) {
@@ -55,10 +55,12 @@
}
/** The key of the scene we should switch to when swiping left. */
- val leftDestinationSceneKey: SceneKey? =
- if (communalInteractor.isCommunalEnabled) {
- SceneKey.Communal
- } else {
- null
- }
+ val leftDestinationSceneKey: StateFlow<SceneKey?> =
+ communalSettingsInteractor.isCommunalEnabled
+ .map { available -> if (available) SceneKey.Communal else null }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 5720cc7..eae7789 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -81,7 +81,7 @@
import com.android.internal.widget.CachingIconView;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
import com.android.systemui.bluetooth.BroadcastDialogController;
import com.android.systemui.broadcast.BroadcastSender;
@@ -1309,7 +1309,7 @@
}
@Nullable
- private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
+ private ActivityTransitionAnimator.Controller buildLaunchAnimatorController(
TransitionLayout player) {
if (!(player.getParent() instanceof ViewGroup)) {
// TODO(b/192194319): Throw instead of just logging.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 523414c..35e0271 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -1155,7 +1155,7 @@
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
// TODO(b/311234666): revisit logic once interactions between the hub and
// shade/keyguard state are finalized
- isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB
+ isCommunalShowing -> LOCATION_COMMUNAL_HUB
onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 375a0ce..687f268 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
@@ -400,7 +400,7 @@
void tryToLaunchInAppRoutingIntent(String routeId, View view) {
ComponentName componentName = mLocalMediaManager.getLinkedItemComponentName();
if (componentName != null) {
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
Intent launchIntent = new Intent(ACTION_TRANSFER_MEDIA);
launchIntent.setComponent(componentName);
@@ -412,7 +412,7 @@
}
void tryToLaunchMediaApplication(View view) {
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
Intent launchIntent = getAppLaunchIntent();
if (launchIntent != null) {
@@ -881,7 +881,7 @@
}
void launchBluetoothPairing(View view) {
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
if (controller == null || (mKeyGuardManager != null
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt
index 2ae3a63..e475647 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt
@@ -16,13 +16,18 @@
package com.android.systemui.media.muteawait
-import android.content.Context
+import android.annotation.SuppressLint
import android.media.AudioAttributes.USAGE_MEDIA
import android.media.AudioDeviceAttributes
import android.media.AudioManager
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import java.io.PrintWriter
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -30,14 +35,15 @@
/** A command line interface to manually test [MediaMuteAwaitConnectionManager]. */
@SysUISingleton
class MediaMuteAwaitConnectionCli @Inject constructor(
- commandRegistry: CommandRegistry,
- private val context: Context
-) {
- init {
+ private val commandRegistry: CommandRegistry,
+ private val audioManager: AudioManager,
+) : CoreStartable {
+ override fun start() {
commandRegistry.registerCommand(MEDIA_MUTE_AWAIT_COMMAND) { MuteAwaitCommand() }
}
inner class MuteAwaitCommand : Command {
+ @SuppressLint("MissingPermission")
override fun execute(pw: PrintWriter, args: List<String>) {
val device = AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
@@ -49,8 +55,6 @@
)
val startOrCancel = args[2]
- val audioManager: AudioManager =
- context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
when (startOrCancel) {
START ->
audioManager.muteAwaitConnection(
@@ -65,6 +69,14 @@
"[type] [name] [$START|$CANCEL]")
}
}
+
+ @Module
+ interface StartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(MediaMuteAwaitConnectionCli::class)
+ fun bindsMediaMuteAwaitConnectionCli(impl: MediaMuteAwaitConnectionCli): CoreStartable
+ }
}
private const val MEDIA_MUTE_AWAIT_COMMAND = "media-mute-await"
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
index 64b772b..0dc10f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
@@ -18,9 +18,14 @@
import android.media.INearbyMediaDevicesProvider
import android.media.INearbyMediaDevicesUpdateCallback
-import com.android.systemui.dagger.SysUISingleton
import android.os.IBinder
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.CommandQueue
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import javax.inject.Inject
/**
@@ -30,9 +35,9 @@
*/
@SysUISingleton
class NearbyMediaDevicesManager @Inject constructor(
- commandQueue: CommandQueue,
+ private val commandQueue: CommandQueue,
private val logger: NearbyMediaDevicesLogger
-) {
+) : CoreStartable {
private var providers: MutableList<INearbyMediaDevicesProvider> = mutableListOf()
private var activeCallbacks: MutableList<INearbyMediaDevicesUpdateCallback> = mutableListOf()
@@ -69,7 +74,7 @@
}
}
- init {
+ override fun start() {
commandQueue.addCallback(commandQueueCallbacks)
}
@@ -108,4 +113,12 @@
}
}
}
+
+ @Module
+ interface StartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(NearbyMediaDevicesManager::class)
+ fun bindsNearbyMediaDevicesManager(impl: NearbyMediaDevicesManager): CoreStartable
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 0167287..aa03e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -395,7 +395,7 @@
}
if (displayId == mDisplayId) {
mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
- BarTransitions.MODE_TRANSPARENT, navbarColorManagedByIme);
+ mTransitionMode, navbarColorManagedByIme);
}
if (mBehavior != behavior) {
mBehavior = behavior;
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
index 2751072..3671dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -16,6 +16,9 @@
package com.android.systemui.process;
+import android.os.Process;
+import android.os.UserHandle;
+
import javax.inject.Inject;
/**
@@ -30,6 +33,15 @@
* Returns {@code true} if System User is running the current process.
*/
public boolean isSystemUser() {
- return android.os.Process.myUserHandle().isSystem();
+ return myUserHandle().isSystem();
+ }
+
+ /**
+ * Returns {@link UserHandle} as returned statically by {@link Process#myUserHandle()}.
+ *
+ * Please strongly consider using {@link com.android.systemui.settings.UserTracker} instead.
+ */
+ public UserHandle myUserHandle() {
+ return Process.myUserHandle();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 0941a20..3fd9803 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -11,7 +11,7 @@
import androidx.annotation.WorkerThread
import com.android.internal.R
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.appops.AppOpsController
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
@@ -175,7 +175,7 @@
startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK
uiExecutor.execute {
activityStarter.startActivity(startSafetyCenter, true,
- ActivityLaunchAnimator.Controller.fromView(privacyChip))
+ ActivityTransitionAnimator.Controller.fromView(privacyChip))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index a2dfc01..c0d9644 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -157,6 +157,12 @@
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
+ } else {
+ // Don't measure the customizer with all the children, it will be measured separately
+ if (child != mQSCustomizer) {
+ super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
+ parentHeightMeasureSpec, heightUsed);
+ }
}
}
@@ -248,6 +254,25 @@
+ mHeader.getHeight();
}
+ // These next two methods are used with Scene container to determine the size of QQS and QS .
+
+ /**
+ * Returns the size of the QQS container, regardless of the measured size of this view.
+ * @return size in pixels of QQS
+ */
+ public int getQqsHeight() {
+ return mHeader.getHeight();
+ }
+
+ /**
+ * Returns the size of QS (or the QSCustomizer), regardless of the measured size of this view
+ * @return size in pixels of QS (or QSCustomizer)
+ */
+ public int getQsHeight() {
+ return mQSCustomizer.isCustomizing() ? mQSCustomizer.getMeasuredHeight()
+ : mQSPanel.getMeasuredHeight();
+ }
+
public void setExpansion(float expansion) {
mQsExpansion = expansion;
if (mQSPanelContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 290821e..4d55714 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -984,6 +984,14 @@
return mListeningAndVisibilityLifecycleOwner;
}
+ public int getQQSHeight() {
+ return mContainer.getQqsHeight();
+ }
+
+ public int getQSHeight() {
+ return mContainer.getQsHeight();
+ }
+
@NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index bd13d06..b53c245 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,7 +51,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -65,14 +65,14 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.DisplayTracker;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-
import dagger.Lazy;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
public class CustomTile extends QSTileImpl<State> implements TileChangeListener,
CustomTileInterface {
public static final String PREFIX = "custom(";
@@ -540,9 +540,9 @@
} else {
Log.i(TAG, "The activity is starting");
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mViewClicked == null ? null :
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
mViewClicked,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 529d684..35cac4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -57,7 +57,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
@@ -70,7 +70,6 @@
import com.android.systemui.qs.logging.QSLogger;
import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* Base quick-settings tile, extend this to create a new tile.
@@ -412,8 +411,8 @@
* @param view The view from which the opening window will be animated.
*/
protected void handleLongClick(@Nullable View view) {
- ActivityLaunchAnimator.Controller animationController =
- view != null ? ActivityLaunchAnimator.Controller.fromView(view,
+ ActivityTransitionAnimator.Controller animationController =
+ view != null ? ActivityTransitionAnimator.Controller.fromView(view,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
animationController);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index d98141f..688f3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -14,7 +14,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -75,7 +75,7 @@
override fun handleClick(view: View?) {
val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
}
val pendingIntent = lastAlarmInfo?.showIntent
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a698208..690b711 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -38,7 +38,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
@@ -222,7 +222,7 @@
mContext,
ROUTE_TYPE_REMOTE_DISPLAY,
v -> {
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(v);
if (controller == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 91b2d97..bb175e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -26,7 +26,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
@@ -112,7 +112,7 @@
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index f70e27d..de9a08e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -27,8 +27,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -40,6 +39,7 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import javax.inject.Inject;
@@ -108,8 +108,8 @@
return;
}
- ActivityLaunchAnimator.Controller animationController =
- view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+ ActivityTransitionAnimator.Controller animationController =
+ view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mActivityStarter.startActivity(intent, true /* dismissShade */,
animationController, true /* showOverLockscreenWhenLocked */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 3b8fb26..1b73225 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -43,7 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -132,8 +132,8 @@
@Override
protected void handleClick(@Nullable View view) {
- ActivityLaunchAnimator.Controller animationController =
- view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+ ActivityTransitionAnimator.Controller animationController =
+ view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mUiHandler.post(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index fe10eaa..7192f58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -22,7 +22,7 @@
import android.os.UserHandle
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
@@ -53,9 +53,9 @@
) : QSTileIntentUserInputHandler {
override fun handle(view: View?, intent: Intent) {
- val animationController: ActivityLaunchAnimator.Controller? =
+ val animationController: ActivityTransitionAnimator.Controller? =
view?.let {
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
it,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
)
@@ -70,9 +70,9 @@
requestLaunchingDefaultActivity: Boolean
) {
if (pendingIntent.isActivity) {
- val animationController: ActivityLaunchAnimator.Controller? =
+ val animationController: ActivityTransitionAnimator.Controller? =
view?.let {
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
it,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 211b4594..41de65c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,7 +75,7 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
@@ -748,7 +748,7 @@
}
private void startActivity(Intent intent, View view) {
- ActivityLaunchAnimator.Controller controller =
+ ActivityTransitionAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
if (controller == null && mCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 0d43396..3d12eed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -28,6 +28,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.QSContainerImpl
import com.android.systemui.qs.QSImpl
import com.android.systemui.qs.dagger.QSSceneComponent
import com.android.systemui.res.R
@@ -68,6 +69,15 @@
/** Set the current state for QS. [state]. */
fun setState(state: State)
+ /** The current height of QQS in the current [qsView], or 0 if there's no view. */
+ val qqsHeight: Int
+
+ /**
+ * The current height of QS in the current [qsView], or 0 if there's no view. If customizing, it
+ * will return the height allocated to the customizer.
+ */
+ val qsHeight: Int
+
sealed class State(
val isVisible: Boolean,
val expansion: Float,
@@ -121,6 +131,11 @@
val qsImpl = _qsImpl.asStateFlow()
override val qsView: Flow<View> = _qsImpl.map { it?.view }.filterNotNull()
+ override val qqsHeight: Int
+ get() = qsImpl.value?.qqsHeight ?: 0
+ override val qsHeight: Int
+ get() = qsImpl.value?.qsHeight ?: 0
+
// Same config changes as in FragmentHostManager
private val interestingChanges =
InterestingConfigChanges(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index fcbe9a6..356eb85 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -45,8 +45,8 @@
sceneKeys =
listOf(
SceneKey.Gone,
- SceneKey.Shade,
SceneKey.QuickSettings,
+ SceneKey.Shade,
),
initialSceneKey = SceneKey.Gone,
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 0f3acaf..c7d3a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -69,8 +69,8 @@
SceneKey.Communal,
SceneKey.Lockscreen,
SceneKey.Bouncer,
- SceneKey.Shade,
SceneKey.QuickSettings,
+ SceneKey.Shade,
),
initialSceneKey = SceneKey.Lockscreen,
)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt
index 76d1d3d..6199a83 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/SecureSettingsRepositoryModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 1c7cc00..df845f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -35,6 +35,7 @@
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -105,13 +106,9 @@
*/
private var shadeShowing = false
- /** Returns true if the glanceable hub is enabled and the container view can be created. */
- fun isEnabled(): Boolean {
- return communalInteractor.isCommunalEnabled && isComposeAvailable()
- }
-
- /** Returns a {@link StateFlow} that tracks whether communal hub is available. */
- fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
+ /** Returns a flow that tracks whether communal hub is available. */
+ fun communalAvailable(): Flow<Boolean> =
+ if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false)
/**
* Creates the container view containing the glanceable hub UI.
@@ -127,9 +124,6 @@
/** Override for testing. */
@VisibleForTesting
internal fun initView(containerView: View): View {
- if (!isEnabled()) {
- throw RuntimeException("Glanceable hub is not enabled")
- }
if (communalContainerView != null) {
throw RuntimeException("Communal view has already been initialized")
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5582a9f..353e143 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -112,7 +112,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -259,7 +259,7 @@
/** The parallax amount of the quick settings translation when dragging down the panel. */
public static final float QS_PARALLAX_AMOUNT = 0.175f;
private static final long ANIMATION_DELAY_ICON_FADE_IN =
- ActivityLaunchAnimator.TIMINGS.getTotalDuration()
+ ActivityTransitionAnimator.TIMINGS.getTotalDuration()
- CollapsedStatusBarFragment.FADE_IN_DURATION
- CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private static final int NO_FIXED_DURATION = -1;
@@ -1348,7 +1348,7 @@
}
updateClockAppearance();
mQsController.updateQsState();
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!KeyguardShadeMigrationNssl.isEnabled() && !FooterViewRefactor.isEnabled()) {
mNotificationStackScrollLayoutController.updateFooter();
}
}
@@ -3231,7 +3231,7 @@
@Override
public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
+ boolean hideIcons = TransitionAnimator.getProgress(ActivityTransitionAnimator.TIMINGS,
linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
if (hideIcons != mHideIconsDuringLaunchAnimation) {
mHideIconsDuringLaunchAnimation = hideIcons;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index ef820f3..aa2d606 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -34,7 +34,7 @@
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
@@ -679,7 +679,7 @@
void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
// TODO(b/288507023): Remove this log.
- if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
}
if (running) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index b64e0b7..91340be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -26,7 +26,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -190,8 +190,8 @@
/** */
@Provides
@SysUISingleton
- static ActivityLaunchAnimator provideActivityLaunchAnimator() {
- return new ActivityLaunchAnimator();
+ static ActivityTransitionAnimator provideActivityTransitionAnimator() {
+ return new ActivityTransitionAnimator();
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
index 6f4a7cd..4af8cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
@@ -2,7 +2,7 @@
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.animation.TransitionAnimator
import kotlin.math.min
@@ -58,7 +58,11 @@
}
fun getProgress(delay: Long, duration: Long): Float {
- return TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, linearProgress, delay,
- duration)
+ return TransitionAnimator.getProgress(
+ ActivityTransitionAnimator.TIMINGS,
+ linearProgress,
+ delay,
+ duration
+ )
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 8fc9619..02d1e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -19,7 +19,7 @@
import android.util.Log
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -55,9 +55,9 @@
}
/**
- * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
- * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
- * notification expanding into an opening window.
+ * An [ActivityTransitionAnimator.Controller] that animates an [ExpandableNotificationRow]. An
+ * instance of this class can be passed to [ActivityTransitionAnimator.startIntentWithAnimation] to
+ * animate a notification expanding into an opening window.
*/
class NotificationLaunchAnimatorController(
private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
@@ -66,7 +66,7 @@
private val notification: ExpandableNotificationRow,
private val jankMonitor: InteractionJankMonitor,
private val onFinishAnimationCallback: Runnable?
-) : ActivityLaunchAnimator.Controller {
+) : ActivityTransitionAnimator.Controller {
companion object {
const val ANIMATION_DURATION_TOP_ROUNDING = 100L
@@ -140,7 +140,7 @@
}
override fun onIntentStarted(willAnimate: Boolean) {
- if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
}
notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate)
@@ -173,8 +173,8 @@
headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
}
- override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
- if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "onLaunchAnimationCancelled()")
}
@@ -194,7 +194,7 @@
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "onLaunchAnimationEnd()")
}
jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 6e2beb4..8b0b973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -56,8 +56,8 @@
if (FooterViewRefactor.isEnabled) {
activeNotificationsInteractor.setNotifStats(notifStats)
}
- // TODO(b/293167744): This shouldn't be done if the footer flag is on, once the footer
- // visibility is handled in the new stack.
+ // TODO(b/293167744): This shouldn't be done if the footer flag is on, once the silent
+ // section clear action is handled in the new stack.
controller.setNotifStats(notifStats)
if (NotificationIconContainerRefactor.isEnabled || FooterViewRefactor.isEnabled) {
renderListInteractor.setRenderedList(entries)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
index 22ce4f1..a3189a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.util.Log
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
import javax.inject.Inject
@@ -40,7 +40,7 @@
/** Sets whether the notification expansion launch animation is currently running. */
fun setIsLaunchAnimationRunning(running: Boolean) {
- if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
Log.d(TAG, "setIsLaunchAnimationRunning(running=$running)")
}
repository.isLaunchAnimationRunning.value = running
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationSettingsInteractorModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationSettingsInteractorModule.kt
index 0a9e12a..ccf6f40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationSettingsInteractorModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationSettingsInteractorModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 5111c11..b23ef35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -52,6 +52,9 @@
isVisible =
activeNotificationsInteractor.hasClearableNotifications
.sample(
+ // TODO(b/322167853): This check is currently duplicated in
+ // NotificationListViewModel, but instead it should be a field in
+ // ShadeAnimationInteractor.
combine(
shadeInteractor.isShadeFullyExpanded,
shadeInteractor.isShadeTouchable,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index d903f06..8768ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -29,6 +29,8 @@
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -56,6 +58,8 @@
panelTouchesEnabled && isKeyguardVisible
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** Amount of a "white" tint to be applied to the icons. */
val tintAlpha: Flow<Float> =
@@ -70,6 +74,8 @@
aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** Are notification icons animated (ex: animated gif)? */
val areIconAnimationsEnabled: Flow<Boolean> =
@@ -78,8 +84,10 @@
// Don't animate icons when we're on AOD / dozing
it != KeyguardState.AOD && it != KeyguardState.DOZING
}
- .flowOn(bgContext)
.onStart { emit(true) }
+ .flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
@@ -91,4 +99,6 @@
)
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index 3574828..260cccd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -21,6 +21,8 @@
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -56,4 +58,6 @@
)
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 38921c2..a64f888 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -35,6 +35,7 @@
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
@@ -64,10 +65,13 @@
panelTouchesEnabled && !isKeyguardShowing
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** The colors with which to display the notification icons. */
val iconColors: Flow<NotificationIconColorLookup> =
- combine(darkIconInteractor.tintAreas, darkIconInteractor.tintColor) { areas, tint ->
+ darkIconInteractor.darkState
+ .map { (areas: Collection<Rect>, tint: Int) ->
NotificationIconColorLookup { viewBounds: Rect ->
if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
IconColorsImpl(tint, areas)
@@ -77,6 +81,8 @@
}
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
@@ -88,6 +94,8 @@
)
}
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
/** An Icon to show "isolated" in the IconContainer. */
val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
@@ -99,6 +107,8 @@
}
.distinctUntilChanged()
.flowOn(bgContext)
+ .conflate()
+ .distinctUntilChanged()
.pairwise(initialValue = null)
.sample(shadeInteractor.shadeExpansion) { (prev, iconInfo), shadeExpansion ->
val animate =
@@ -113,7 +123,7 @@
/** Location to show an isolated icon, if there is one. */
val isolatedIconLocation: Flow<Rect> =
- headsUpIconInteractor.isolatedIconLocation.filterNotNull()
+ headsUpIconInteractor.isolatedIconLocation.filterNotNull().conflate().distinctUntilChanged()
private class IconColorsImpl(
override val tint: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 0a11eb2..42b56e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -752,6 +752,7 @@
}
public void setIsRemoteInputActive(boolean isActive) {
+ FooterViewRefactor.assertInLegacyMode();
mIsRemoteInputActive = isActive;
updateFooter();
}
@@ -764,6 +765,7 @@
@VisibleForTesting
public void updateFooter() {
+ FooterViewRefactor.assertInLegacyMode();
if (mFooterView == null || mController == null) {
return;
}
@@ -776,10 +778,12 @@
}
private boolean shouldShowDismissView() {
+ FooterViewRefactor.assertInLegacyMode();
return mController.hasActiveClearableNotifications(ROWS_ALL);
}
private boolean shouldShowFooterView(boolean showDismissView) {
+ FooterViewRefactor.assertInLegacyMode();
return (showDismissView || mController.getVisibleNotificationCount() > 0)
&& mIsCurrentUserSetup // see: b/193149550
&& !onKeyguard()
@@ -4359,6 +4363,12 @@
layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
}
if (endPosition > layoutEnd) {
+ // if Scene Container is active, send bottom notification expansion delta
+ // to it so that it can scroll the stack and scrim accordingly.
+ if (SceneContainerFlag.isEnabled()) {
+ float diff = endPosition - layoutEnd;
+ mController.sendSyntheticScrollToSceneFramework(diff);
+ }
setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
}
@@ -4723,9 +4733,6 @@
footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
});
}
- if (FooterViewRefactor.isEnabled()) {
- updateFooter();
- }
}
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -4790,16 +4797,15 @@
}
public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
+ FooterViewRefactor.assertInLegacyMode();
if (mFooterView == null || mNotificationStackSizeCalculator == null) {
return;
}
boolean animate = mIsExpanded && mAnimationsEnabled;
mFooterView.setVisible(visible, animate);
- if (!FooterViewRefactor.isEnabled()) {
- mFooterView.showHistory(showHistory);
- mFooterView.setClearAllButtonVisible(showDismissView, animate);
- mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
- }
+ mFooterView.showHistory(showHistory);
+ mFooterView.setClearAllButtonVisible(showDismissView, animate);
+ mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
}
@VisibleForTesting
@@ -5070,7 +5076,7 @@
if (mOwnScrollY > 0) {
setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
}
- if (footerAffected) {
+ if (!FooterViewRefactor.isEnabled() && footerAffected) {
updateFooter();
}
}
@@ -5081,6 +5087,10 @@
}
private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
+ // If scene container is active, NSSL should not control its own scrolling.
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
// Avoid Flicking during clear all
// when the shade finishes closing, onExpansionStopped will call
// resetScrollPosition to setOwnScrollY to 0
@@ -5176,6 +5186,7 @@
}
void setUpcomingStatusBarState(int upcomingStatusBarState) {
+ FooterViewRefactor.assertInLegacyMode();
mUpcomingStatusBarState = upcomingStatusBarState;
if (mUpcomingStatusBarState != mStatusBarState) {
updateFooter();
@@ -5193,7 +5204,9 @@
setDimmed(onKeyguard, fromShadeLocked);
setExpandingEnabled(!onKeyguard);
- updateFooter();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateFooter();
+ }
requestChildrenUpdate();
onUpdateRowStates();
updateVisibility();
@@ -5270,8 +5283,11 @@
for (int i = 0; i < childCount; i++) {
ExpandableView child = getChildAtIndex(i);
child.dump(pw, args);
- if (child instanceof FooterView) {
- DumpUtilsKt.withIncreasedIndent(pw, () -> dumpFooterViewVisibility(pw));
+ if (!FooterViewRefactor.isEnabled()) {
+ if (child instanceof FooterView) {
+ DumpUtilsKt.withIncreasedIndent(pw,
+ () -> dumpFooterViewVisibility(pw));
+ }
}
pw.println();
}
@@ -5290,6 +5306,7 @@
}
private void dumpFooterViewVisibility(IndentingPrintWriter pw) {
+ FooterViewRefactor.assertInLegacyMode();
final boolean showDismissView = shouldShowDismissView();
pw.println("showFooterView: " + shouldShowFooterView(showDismissView));
@@ -5988,6 +6005,7 @@
* Sets whether the current user is set up, which is required to show the footer (b/193149550)
*/
public void setCurrentUserSetup(boolean isCurrentUserSetup) {
+ FooterViewRefactor.assertInLegacyMode();
if (mIsCurrentUserSetup != isCurrentUserSetup) {
mIsCurrentUserSetup = isCurrentUserSetup;
updateFooter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a2ff406..5fa0624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -314,8 +314,10 @@
// The bottom might change because we're using the final actual height of the view
mView.setAnimateBottomOnLayout(true);
}
- // Let's update the footer once the notifications have been updated (in the next frame)
- mView.post(this::updateFooter);
+ if (!FooterViewRefactor.isEnabled()) {
+ // Let's update the footer once the notifications have been updated (in the next frame)
+ mView.post(this::updateFooter);
+ }
};
@VisibleForTesting
@@ -342,8 +344,8 @@
mView.reinflateViews();
if (!FooterViewRefactor.isEnabled()) {
updateShowEmptyShadeView();
+ updateFooter();
}
- updateFooter();
}
@Override
@@ -389,7 +391,9 @@
@Override
public void onUpcomingStateChanged(int newState) {
- mView.setUpcomingStatusBarState(newState);
+ if (!FooterViewRefactor.isEnabled()) {
+ mView.setUpcomingStatusBarState(newState);
+ }
}
@Override
@@ -407,7 +411,9 @@
public void onUserChanged(int userId) {
updateSensitivenessWithAnimation(false);
mHistoryEnabled = null;
- updateFooter();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateFooter();
+ }
}
};
@@ -810,14 +816,14 @@
if (!FooterViewRefactor.isEnabled()) {
mView.setFooterClearAllListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
+ mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputActive(boolean active) {
+ mView.setIsRemoteInputActive(active);
+ }
+ });
}
- mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
- mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
- @Override
- public void onRemoteInputActive(boolean active) {
- mView.setIsRemoteInputActive(active);
- }
- });
mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> {
final Runnable doCollapseRunnable = () ->
mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
@@ -871,7 +877,9 @@
switch (key) {
case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
mHistoryEnabled = null; // invalidate
- updateFooter();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateFooter();
+ }
break;
case HIGH_PRIORITY:
mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
@@ -893,9 +901,11 @@
return kotlin.Unit.INSTANCE;
});
- // attach callback, and then call it to update mView immediately
- mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- mDeviceProvisionedListener.onDeviceProvisionedChanged();
+ if (!FooterViewRefactor.isEnabled()) {
+ // attach callback, and then call it to update mView immediately
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+ mDeviceProvisionedListener.onDeviceProvisionedChanged();
+ }
if (screenshareNotificationHiding()) {
mSensitiveNotificationProtectionController
@@ -1106,8 +1116,7 @@
}
public int getVisibleNotificationCount() {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle footer
- // visibility in the refactored code
+ FooterViewRefactor.assertInLegacyMode();
return mNotifStats.getNumActiveNotifs();
}
@@ -1142,6 +1151,11 @@
}
}
+ /** Send internal notification expansion to the scene container framework. */
+ public void sendSyntheticScrollToSceneFramework(Float delta) {
+ mStackAppearanceInteractor.setSyntheticScroll(delta);
+ }
+
/** Get the y-coordinate of the top bound of the stack. */
public float getPlaceholderTop() {
return mStackAppearanceInteractor.getStackBounds().getValue().getTop();
@@ -1509,14 +1523,14 @@
* Return whether there are any clearable notifications
*/
public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
- // visibility in the refactored code
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent
+ // section clear action in the new stack.
return hasNotifications(selection, true /* clearable */);
}
public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the footer
- // visibility in the refactored code
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent
+ // section clear action in the new stack.
boolean hasAlertingMatchingClearable = isClearable
? mNotifStats.getHasClearableAlertingNotifs()
: mNotifStats.getHasNonClearableAlertingNotifs();
@@ -1558,7 +1572,9 @@
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
- updateFooter();
+ if (!FooterViewRefactor.isEnabled()) {
+ updateFooter();
+ }
}
public void lockScrollTo(NotificationEntry entry) {
@@ -1573,6 +1589,7 @@
}
public void updateFooter() {
+ FooterViewRefactor.assertInLegacyMode();
Trace.beginSection("NSSLC.updateFooter");
mView.updateFooter();
Trace.endSection();
@@ -2134,19 +2151,16 @@
private class NotifStackControllerImpl implements NotifStackController {
@Override
public void setNotifStats(@NonNull NotifStats notifStats) {
- // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once footer visibility
- // is handled in the refactored stack.
+ // TODO(b/293167744): FooterViewRefactor.assertInLegacyMode() once we handle the silent
+ // section clear action in the new stack.
mNotifStats = notifStats;
if (!FooterViewRefactor.isEnabled()) {
mView.setHasFilteredOutSeenNotifications(
mSeenNotificationsInteractor
.getHasFilteredOutSeenNotifications().getValue());
- }
- updateFooter();
-
- if (!FooterViewRefactor.isEnabled()) {
+ updateFooter();
updateShowEmptyShadeView();
updateImportantForAccessibility();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 664a6b6..15fde0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -137,7 +138,7 @@
}
private void updateAlphaState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
for (ExpandableView view : algorithmState.visibleChildren) {
final ViewState viewState = view.getViewState();
final boolean isHunGoingToShade = ambientState.isShadeExpanded()
@@ -295,7 +296,7 @@
}
private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
- int speedBumpIndex) {
+ int speedBumpIndex) {
int childCount = algorithmState.visibleChildren.size();
int belowSpeedBump = speedBumpIndex;
for (int i = 0; i < childCount; i++) {
@@ -322,7 +323,7 @@
}
private void updateClipping(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY();
float clipStart = 0;
@@ -454,7 +455,7 @@
}
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
- ExpandableView v) {
+ ExpandableView v) {
ExpandableViewState viewState = v.getViewState();
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
@@ -480,7 +481,7 @@
* @param ambientState The current ambient state
*/
protected void updatePositionsForState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
if (!ambientState.isOnKeyguard()
|| (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
algorithmState.mCurrentYPosition += mNotificationScrimPadding;
@@ -494,7 +495,7 @@
}
private void setLocation(ExpandableViewState expandableViewState, float currentYPosition,
- int i) {
+ int i) {
expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
if (currentYPosition <= 0) {
expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
@@ -598,18 +599,31 @@
viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation()
);
if (view instanceof FooterView) {
- final boolean shadeClosed = !ambientState.isShadeExpanded();
- final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
- if (shadeClosed) {
- viewState.hidden = true;
- } else {
+ if (FooterViewRefactor.isEnabled()) {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ view.getIntrinsicHeight();
final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+ // TODO(b/293167744): May be able to keep only noSpaceForFooter here if we add an
+ // emission when clearAllNotifications is called, and then use that in the footer
+ // visibility flow.
((FooterView.FooterViewState) viewState).hideContent =
- isShelfShowing || noSpaceForFooter
- || (ambientState.isClearAllInProgress()
+ noSpaceForFooter || (ambientState.isClearAllInProgress()
&& !hasNonClearableNotifs(algorithmState));
+
+ } else {
+ final boolean shadeClosed = !ambientState.isShadeExpanded();
+ final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
+ if (shadeClosed) {
+ viewState.hidden = true;
+ } else {
+ final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ + view.getIntrinsicHeight();
+ final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+ ((FooterView.FooterViewState) viewState).hideContent =
+ isShelfShowing || noSpaceForFooter
+ || (ambientState.isClearAllInProgress()
+ && !hasNonClearableNotifs(algorithmState));
+ }
}
} else {
if (view instanceof EmptyShadeView) {
@@ -731,7 +745,7 @@
@VisibleForTesting
void updatePulsingStates(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
ExpandableNotificationRow pulsingRow = null;
for (int i = 0; i < childCount; i++) {
@@ -761,7 +775,7 @@
}
private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
// Move the tracked heads up into position during the appear animation, by interpolating
@@ -870,18 +884,18 @@
boolean shouldHunBeVisibleWhenScrolled(boolean mustStayOnScreen, boolean headsUpIsVisible,
boolean showingPulsing, boolean isOnKeyguard, boolean headsUpOnKeyguard) {
return mustStayOnScreen && !headsUpIsVisible
- && !showingPulsing
- && (!isOnKeyguard || headsUpOnKeyguard);
+ && !showingPulsing
+ && (!isOnKeyguard || headsUpOnKeyguard);
}
- /**
+ /**
* When shade is open and we are scrolled to the bottom of notifications,
* clamp incoming HUN in its collapsed form, right below qs offset.
* Transition pinned collapsed HUN to full height when scrolling back up.
*/
@VisibleForTesting
void clampHunToTop(float clampInset, float stackTranslation, float collapsedHeight,
- ExpandableViewState viewState) {
+ ExpandableViewState viewState) {
final float newTranslation = Math.max(clampInset + stackTranslation,
viewState.getYTranslation());
@@ -896,7 +910,7 @@
// Pin HUN to bottom of expanded QS
// while the rest of notifications are scrolled offscreen.
private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
- ExpandableViewState childState) {
+ ExpandableViewState childState) {
float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation();
@@ -919,7 +933,7 @@
@VisibleForTesting
float computeCornerRoundnessForPinnedHun(float hostViewHeight, float stackY,
- float viewMaxHeight, float originalCornerRadius) {
+ float viewMaxHeight, float originalCornerRadius) {
// Compute y where corner roundness should be in its original unpinned state.
// We use view max height because the pinned collapsed HUN expands to max height
@@ -948,7 +962,7 @@
* @param ambientState The ambient state of the algorithm
*/
private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
@@ -976,13 +990,13 @@
* vertically top of screen. Top HUNs should have drop shadows
* @param childrenOnTop It is greater than 0 when there's an existing HUN that is elevated
* @return childrenOnTop The decimal part represents the fraction of the elevated HUN's height
- * that overlaps with QQS Panel. The integer part represents the count of
- * previous HUNs whose Z positions are greater than 0.
+ * that overlaps with QQS Panel. The integer part represents the count of
+ * previous HUNs whose Z positions are greater than 0.
*/
protected float updateChildZValue(int i, float childrenOnTop,
- StackScrollAlgorithmState algorithmState,
- AmbientState ambientState,
- boolean isTopHun) {
+ StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState,
+ boolean isTopHun) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState();
float baseZ = ambientState.getBaseZHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index b38d619..fb14a91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -460,15 +460,8 @@
mHeadsUpAppearChildren.add(changingView);
mTmpState.copyFrom(changingView.getViewState());
- if (event.headsUpFromBottom) {
- // start from the bottom of the screen
- mTmpState.setYTranslation(
- mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
- } else {
- // start from the top of the screen
- mTmpState.setYTranslation(
- -mStackTopMargin - mHeadsUpAppearStartAboveScreen);
- }
+ // translate the HUN in from the top, or the bottom of the screen
+ mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
// set the height and the initial position
mTmpState.applyToView(changingView);
mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
@@ -512,12 +505,20 @@
|| event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
Runnable endRunnable = null;
+ mTmpState.copyFrom(changingView.getViewState());
if (changingView.getParent() == null) {
// This notification was actually removed, so we need to add it
// transiently
mHostLayout.addTransientView(changingView, 0);
changingView.setTransientContainer(mHostLayout);
- mTmpState.initFrom(changingView);
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ // StackScrollAlgorithm cannot find this view because it has been removed
+ // from the NSSL. To correctly translate the view to the top or bottom of
+ // the screen (where it animated from), we need to update its translation.
+ mTmpState.setYTranslation(
+ getHeadsUpYTranslationStart(event.headsUpFromBottom)
+ );
+ }
endRunnable = changingView::removeFromTransientContainer;
}
@@ -565,16 +566,19 @@
changingView.setInRemovalAnimation(true);
};
}
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
- Interpolators.FAST_OUT_SLOW_IN_REVERSE);
- }
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
0, 0.0f, true /* isHeadsUpAppear */,
startAnimation, postAnimation,
getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
+ mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
+ Interpolators.FAST_OUT_SLOW_IN_REVERSE);
+ mAnimationProperties.getAnimationFilter().animateY = true;
+ mTmpState.animateTo(changingView, mAnimationProperties);
+ }
} else if (endRunnable != null) {
endRunnable.run();
}
@@ -585,6 +589,15 @@
return needsCustomAnimation;
}
+ private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) {
+ if (headsUpFromBottom) {
+ // start from the bottom of the screen
+ return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen;
+ }
+ // start from the top of the screen
+ return -mStackTopMargin - mHeadsUpAppearStartAboveScreen;
+ }
+
public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
final boolean isRubberbanded) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
index 311ba83..9efe632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -47,4 +47,11 @@
* further.
*/
val scrolledToTop = MutableStateFlow(true)
+
+ /**
+ * The amount in px that the notification stack should scroll due to internal expansion. This
+ * should only happen when a notification expansion hits the bottom of the screen, so it is
+ * necessary to scroll up to keep expanding the notification.
+ */
+ val syntheticScroll = MutableStateFlow(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 9984ba9..08df473 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -50,6 +51,13 @@
*/
val scrolledToTop: StateFlow<Boolean> = repository.scrolledToTop.asStateFlow()
+ /**
+ * The amount in px that the notification stack should scroll due to internal expansion. This
+ * should only happen when a notification expansion hits the bottom of the screen, so it is
+ * necessary to scroll up to keep expanding the notification.
+ */
+ val syntheticScroll: Flow<Float> = repository.syntheticScroll.asStateFlow()
+
/** Sets the position of the notification stack in the current scene. */
fun setStackBounds(bounds: NotificationContainerBounds) {
check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
@@ -70,4 +78,9 @@
fun setScrolledToTop(scrolledToTop: Boolean) {
repository.scrolledToTop.value = scrolledToTop
}
+
+ /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */
+ fun setSyntheticScroll(delta: Float) {
+ repository.syntheticScroll.value = delta
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 4d65b9d..883aa9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -18,11 +18,10 @@
import android.view.LayoutInflater
import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.traceSection
+import com.android.app.tracing.TraceUtils.traceAsync
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.common.ui.reinflateAndBindLatest
import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -33,6 +32,7 @@
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
+import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
@@ -43,12 +43,18 @@
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.util.kotlin.awaitCancellationThenDispose
import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.value
import java.util.Optional
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
@@ -83,7 +89,7 @@
bindHideList(viewController, viewModel, hiderTracker)
if (FooterViewRefactor.isEnabled) {
- launch { bindFooter(view) }
+ launch { reinflateAndBindFooter(view) }
launch { bindEmptyShade(view) }
launch {
viewModel.isImportantForAccessibility.collect { isImportantForAccessibility
@@ -108,42 +114,61 @@
)
}
- private suspend fun bindFooter(parentView: NotificationStackScrollLayout) {
+ private suspend fun reinflateAndBindFooter(parentView: NotificationStackScrollLayout) {
viewModel.footer.getOrNull()?.let { footerViewModel ->
// The footer needs to be re-inflated every time the theme or the font size changes.
- configuration.reinflateAndBindLatest(
- R.layout.status_bar_notification_footer,
- parentView,
- attachToRoot = false,
- backgroundDispatcher,
- ) { footerView: FooterView ->
- traceSection("bind FooterView") {
- val disposableHandle =
- FooterViewBinder.bindWhileAttached(
- footerView,
- footerViewModel,
- clearAllNotifications = {
- metricsLogger.action(
- MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
- )
- parentView.clearAllNotifications()
- },
- launchNotificationSettings = { view ->
- notificationActivityStarter
- .get()
- .startHistoryIntent(view, /* showHistory = */ false)
- },
- launchNotificationHistory = { view ->
- notificationActivityStarter
- .get()
- .startHistoryIntent(view, /* showHistory = */ true)
- },
- )
- parentView.setFooterView(footerView)
- return@reinflateAndBindLatest disposableHandle
+ configuration
+ .inflateLayout<FooterView>(
+ R.layout.status_bar_notification_footer,
+ parentView,
+ attachToRoot = false,
+ )
+ .flowOn(backgroundDispatcher)
+ .collectLatest { footerView: FooterView ->
+ traceAsync("bind FooterView") {
+ parentView.setFooterView(footerView)
+ bindFooter(footerView, footerViewModel, parentView)
+ }
}
+ }
+ }
+
+ /**
+ * Binds the footer (including its visibility) and dispose of the [DisposableHandle] when done.
+ */
+ private suspend fun bindFooter(
+ footerView: FooterView,
+ footerViewModel: FooterViewModel,
+ parentView: NotificationStackScrollLayout
+ ): Unit = coroutineScope {
+ val disposableHandle =
+ FooterViewBinder.bindWhileAttached(
+ footerView,
+ footerViewModel,
+ clearAllNotifications = {
+ metricsLogger.action(MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES)
+ parentView.clearAllNotifications()
+ },
+ launchNotificationSettings = { view ->
+ notificationActivityStarter
+ .get()
+ .startHistoryIntent(view, /* showHistory = */ false)
+ },
+ launchNotificationHistory = { view ->
+ notificationActivityStarter
+ .get()
+ .startHistoryIntent(view, /* showHistory = */ true)
+ },
+ )
+ launch {
+ viewModel.shouldShowFooterView.collect { animatedVisibility ->
+ footerView.setVisible(
+ /* visible = */ animatedVisibility.value,
+ /* animate = */ animatedVisibility.isAnimating,
+ )
}
}
+ disposableHandle.awaitCancellationThenDispose()
}
private suspend fun bindEmptyShade(parentView: NotificationStackScrollLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index 814146c..a157785 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -69,6 +69,9 @@
controller.setMaxAlphaForExpansion(
((expandFraction - 0.5f) / 0.5f).coerceAtLeast(0f)
)
+ if (expandFraction == 0f || expandFraction == 1f) {
+ controller.onExpansionStopped()
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 86c0a678..a6c6586 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -16,21 +16,32 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
+import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.util.kotlin.combine
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.AnimatedValue
+import com.android.systemui.util.ui.toAnimatedValueFlow
import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** ViewModel for the list of notifications. */
@@ -42,9 +53,13 @@
val footer: Optional<FooterViewModel>,
val logger: Optional<NotificationLoggerViewModel>,
activeNotificationsInteractor: ActiveNotificationsInteractor,
+ keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ powerInteractor: PowerInteractor,
+ remoteInputInteractor: RemoteInputInteractor,
seenNotificationsInteractor: SeenNotificationsInteractor,
shadeInteractor: ShadeInteractor,
+ userSetupInteractor: UserSetupInteractor,
zenModeInteractor: ZenModeInteractor,
) {
/**
@@ -76,6 +91,10 @@
combine(
activeNotificationsInteractor.areAnyNotificationsPresent,
shadeInteractor.isQsFullscreen,
+ // TODO(b/293167744): It looks like we're essentially trying to check the same
+ // things for the empty shade visibility as we do for the footer, just in a
+ // slightly different way. We should change this so we also check
+ // statusBarState and isAwake instead of specific keyguard transitions.
keyguardTransitionInteractor.isInTransitionToState(KeyguardState.AOD).onStart {
emit(false)
},
@@ -97,6 +116,80 @@
}
}
+ val shouldShowFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+ flowOf(AnimatedValue.NotAnimating(false))
+ } else {
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ userSetupInteractor.isUserSetUp,
+ keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD },
+ shadeInteractor.qsExpansion,
+ shadeInteractor.isQsFullscreen,
+ powerInteractor.isAsleep,
+ remoteInputInteractor.isRemoteInputActive,
+ shadeInteractor.shadeExpansion.map { it == 0f }
+ ) {
+ hasNotifications,
+ isUserSetUp,
+ isOnKeyguard,
+ qsExpansion,
+ qsFullScreen,
+ isAsleep,
+ isRemoteInputActive,
+ isShadeClosed ->
+ Pair(
+ // Should the footer be visible?
+ when {
+ !hasNotifications -> false
+ // Hide the footer until the user setup is complete, to prevent access
+ // to settings (b/193149550).
+ !isUserSetUp -> false
+ // Do not show the footer if the lockscreen is visible (incl. AOD),
+ // except if the shade is opened on top. See also b/219680200.
+ isOnKeyguard -> false
+ // Make sure we're not showing the footer in the transition to AOD while
+ // going to sleep (b/190227875). The StatusBarState is unfortunately not
+ // updated quickly enough when the power button is pressed, so this is
+ // necessary in addition to the isOnKeyguard check.
+ isAsleep -> false
+ // Do not show the footer if quick settings are fully expanded (except
+ // for the foldable split shade view). See b/201427195 && b/222699879.
+ qsExpansion == 1f && qsFullScreen -> false
+ // Hide the footer if remote input is active (i.e. user is replying to a
+ // notification). See b/75984847.
+ isRemoteInputActive -> false
+ // Never show the footer if the shade is collapsed (e.g. when HUNing).
+ isShadeClosed -> false
+ else -> true
+ },
+ // This could in theory be in the .sample below, but it tends to be
+ // inconsistent, so we're passing it on to make sure we have the same state.
+ isOnKeyguard
+ )
+ }
+ .distinctUntilChanged()
+ // Should we animate the visibility change?
+ .sample(
+ // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
+ // but instead it should be a field in ShadeAnimationInteractor.
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { (visible, isOnKeyguard), (isShadeFullyExpanded, animationsEnabled) ->
+ // Animate if the shade is interactive, but NOT on the lockscreen. Having
+ // animations enabled while on the lockscreen makes the footer appear briefly
+ // when transitioning between the shade and keyguard.
+ val shouldAnimate = isShadeFullyExpanded && animationsEnabled && !isOnKeyguard
+ AnimatableEvent(visible, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ }
+ }
+
// TODO(b/308591475): This should be tracked separately by the empty shade.
val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index bdf1a64..3a0f03f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
@SysUISingleton
@@ -45,31 +46,32 @@
*/
val expandFraction: Flow<Float> =
combine(
- shadeInteractor.shadeExpansion,
- sceneInteractor.transitionState,
- ) { shadeExpansion, transitionState ->
- when (transitionState) {
- is ObservableTransitionState.Idle -> {
- if (transitionState.scene == SceneKey.Lockscreen) {
- 1f
- } else {
- shadeExpansion
+ shadeInteractor.shadeExpansion,
+ sceneInteractor.transitionState,
+ ) { shadeExpansion, transitionState ->
+ when (transitionState) {
+ is ObservableTransitionState.Idle -> {
+ if (transitionState.scene == SceneKey.Lockscreen) {
+ 1f
+ } else {
+ shadeExpansion
+ }
}
- }
- is ObservableTransitionState.Transition -> {
- if (
- (transitionState.fromScene == SceneKey.Shade &&
- transitionState.toScene == SceneKey.QuickSettings) ||
- (transitionState.fromScene == SceneKey.QuickSettings &&
- transitionState.toScene == SceneKey.Shade)
- ) {
- 1f
- } else {
- shadeExpansion
+ is ObservableTransitionState.Transition -> {
+ if (
+ (transitionState.fromScene == SceneKey.Shade &&
+ transitionState.toScene == SceneKey.QuickSettings) ||
+ (transitionState.fromScene == SceneKey.QuickSettings &&
+ transitionState.toScene == SceneKey.Shade)
+ ) {
+ 1f
+ } else {
+ shadeExpansion
+ }
}
}
}
- }
+ .distinctUntilChanged()
/** The bounds of the notification stack in the current scene. */
val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 65d9c9f..7ac5cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -86,6 +86,13 @@
*/
val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
+ /**
+ * The amount in px that the notification stack should scroll due to internal expansion. This
+ * should only happen when a notification expansion hits the bottom of the screen, so it is
+ * necessary to scroll up to keep expanding the notification.
+ */
+ val syntheticScroll: Flow<Float> = interactor.syntheticScroll
+
/** Sets the y-coord in px of the top of the contents of the notification stack. */
fun onContentTopChanged(padding: Float) {
interactor.setContentTop(padding)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index b49af0e..80d45fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -30,8 +30,8 @@
import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator.PendingIntentStarter
import com.android.systemui.animation.DelegateLaunchAnimatorController
import com.android.systemui.assist.AssistManager
import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent
@@ -75,7 +75,7 @@
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
private val context: Context,
@DisplayId private val displayId: Int,
private val lockScreenUserManager: NotificationLockscreenUserManager,
@@ -127,7 +127,7 @@
override fun startPendingIntentDismissingKeyguard(
intent: PendingIntent,
intentSentUiThreadCallback: Runnable?,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
) {
activityStarterInternal.startPendingIntentDismissingKeyguard(
intent = intent,
@@ -139,7 +139,7 @@
override fun startPendingIntentMaybeDismissingKeyguard(
intent: PendingIntent,
intentSentUiThreadCallback: Runnable?,
- animationController: ActivityLaunchAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?
) {
activityStarterInternal.startPendingIntentDismissingKeyguard(
intent = intent,
@@ -209,7 +209,7 @@
override fun startActivity(
intent: Intent,
dismissShade: Boolean,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreenWhenLocked: Boolean,
) {
activityStarterInternal.startActivity(
@@ -222,7 +222,7 @@
override fun startActivity(
intent: Intent,
dismissShade: Boolean,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreenWhenLocked: Boolean,
userHandle: UserHandle?,
) {
@@ -245,7 +245,7 @@
override fun postStartActivityDismissingKeyguard(
intent: PendingIntent,
- animationController: ActivityLaunchAnimator.Controller?
+ animationController: ActivityTransitionAnimator.Controller?
) {
postOnUiThread {
activityStarterInternal.startPendingIntentDismissingKeyguard(
@@ -268,7 +268,7 @@
override fun postStartActivityDismissingKeyguard(
intent: Intent,
delay: Int,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
) {
postOnUiThread(delay) {
activityStarterInternal.startActivityDismissingKeyguard(
@@ -283,7 +283,7 @@
override fun postStartActivityDismissingKeyguard(
intent: Intent,
delay: Int,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
customMessage: String?,
) {
postOnUiThread(delay) {
@@ -342,7 +342,7 @@
disallowEnterPictureInPictureWhileLaunching: Boolean,
callback: ActivityStarter.Callback?,
flags: Int,
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
userHandle: UserHandle?,
) {
activityStarterInternal.startActivityDismissingKeyguard(
@@ -430,7 +430,7 @@
disallowEnterPictureInPictureWhileLaunching: Boolean = false,
callback: ActivityStarter.Callback? = null,
flags: Int = 0,
- animationController: ActivityLaunchAnimator.Controller? = null,
+ animationController: ActivityTransitionAnimator.Controller? = null,
userHandle: UserHandle? = null,
customMessage: String? = null,
) {
@@ -464,7 +464,7 @@
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
intent.addFlags(flags)
val result = intArrayOf(ActivityManager.START_CANCELED)
- activityLaunchAnimator.startIntentWithAnimation(
+ activityTransitionAnimator.startIntentWithAnimation(
animController,
animate,
intent.getPackage()
@@ -552,7 +552,7 @@
intent: PendingIntent,
intentSentUiThreadCallback: Runnable? = null,
associatedView: View? = null,
- animationController: ActivityLaunchAnimator.Controller? = null,
+ animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreen: Boolean = false,
) {
val animationController =
@@ -602,7 +602,7 @@
val collapse = !animate
val runnable = Runnable {
try {
- activityLaunchAnimator.startPendingIntentWithAnimation(
+ activityTransitionAnimator.startPendingIntentWithAnimation(
controller,
animate,
intent.creatorPackage,
@@ -670,7 +670,7 @@
fun startActivity(
intent: Intent,
dismissShade: Boolean = false,
- animationController: ActivityLaunchAnimator.Controller? = null,
+ animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreenWhenLocked: Boolean = false,
userHandle: UserHandle? = null,
) {
@@ -698,7 +698,7 @@
showOverLockscreenWhenLocked
) == true
- var controller: ActivityLaunchAnimator.Controller? = null
+ var controller: ActivityTransitionAnimator.Controller? = null
if (animate) {
// Wrap the animation controller to dismiss the shade and set
// mIsLaunchingActivityOverLockscreen during the animation.
@@ -721,7 +721,7 @@
centralSurfaces?.awakenDreams()
}
- activityLaunchAnimator.startIntentWithAnimation(
+ activityTransitionAnimator.startIntentWithAnimation(
controller,
animate,
intent.getPackage(),
@@ -815,7 +815,7 @@
}
/**
- * Return a [ActivityLaunchAnimator.Controller] wrapping `animationController` so that:
+ * Return a [ActivityTransitionAnimator.Controller] wrapping `animationController` so that:
* - if it launches in the notification shade window and `dismissShade` is true, then the
* shade will be instantly dismissed at the end of the animation.
* - if it launches in status bar window, it will make the status bar window match the
@@ -830,15 +830,15 @@
* @param isLaunchForActivity whether the launch is for an activity.
*/
private fun wrapAnimationControllerForShadeOrStatusBar(
- animationController: ActivityLaunchAnimator.Controller?,
+ animationController: ActivityTransitionAnimator.Controller?,
dismissShade: Boolean,
isLaunchForActivity: Boolean,
- ): ActivityLaunchAnimator.Controller? {
+ ): ActivityTransitionAnimator.Controller? {
if (animationController == null) {
return null
}
val rootView = animationController.transitionContainer.rootView
- val controllerFromStatusBar: Optional<ActivityLaunchAnimator.Controller> =
+ val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
statusBarWindowController.wrapAnimationControllerIfInStatusBar(
rootView,
animationController
@@ -870,8 +870,8 @@
* lockscreen, the correct flags are set for it to be occluded.
*/
private fun wrapAnimationControllerForLockscreen(
- animationController: ActivityLaunchAnimator.Controller?
- ): ActivityLaunchAnimator.Controller? {
+ animationController: ActivityTransitionAnimator.Controller?
+ ): ActivityTransitionAnimator.Controller? {
return animationController?.let {
object : DelegateLaunchAnimatorController(it) {
override fun onIntentStarted(willAnimate: Boolean) {
@@ -912,7 +912,9 @@
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
}
- override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ override fun onTransitionAnimationCancelled(
+ newKeyguardOccludedState: Boolean?
+ ) {
if (newKeyguardOccludedState != null) {
keyguardViewMediatorLazy
.get()
@@ -925,7 +927,7 @@
// collapse the shade (or at least run the // post collapse
// runnables) later on.
centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
- delegate.onLaunchAnimationCancelled(newKeyguardOccludedState)
+ delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 4019436..9052409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -38,7 +38,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.display.data.repository.DisplayMetricsRepository;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -334,6 +334,6 @@
/**
* Gets an animation controller from a notification row.
*/
- ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification(
+ ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification(
ExpandableNotificationRow associatedView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 60dfaa7..8af7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -20,7 +20,7 @@
import android.view.MotionEvent
import androidx.lifecycle.LifecycleRegistry
import com.android.keyguard.AuthKeyguardMessageArea
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.navigationbar.NavigationBarView
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.qs.QSPanelController
@@ -99,5 +99,5 @@
override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
override fun getAnimatorControllerFromNotification(
associatedView: ExpandableNotificationRow?,
- ): ActivityLaunchAnimator.Controller? = null
+ ): ActivityTransitionAnimator.Controller? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 3e7089c..35aa3df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -115,7 +115,7 @@
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.biometrics.AuthRippleController;
@@ -576,7 +576,7 @@
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
- private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final ActivityTransitionAnimator mActivityTransitionAnimator;
private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
private final Lazy<NotificationPresenter> mPresenterLazy;
private final Lazy<NotificationActivityStarter> mNotificationActivityStarterLazy;
@@ -694,7 +694,7 @@
@Main MessageRouter messageRouter,
WallpaperManager wallpaperManager,
Optional<StartingSurface> startingSurfaceOptional,
- ActivityLaunchAnimator activityLaunchAnimator,
+ ActivityTransitionAnimator activityTransitionAnimator,
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
IDreamManager dreamManager,
@@ -816,7 +816,7 @@
shadeExpansionListener.onPanelExpansionChanged(currentState);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
- mActivityLaunchAnimator = activityLaunchAnimator;
+ mActivityTransitionAnimator = activityTransitionAnimator;
// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1424,8 +1424,8 @@
private void setUpPresenter() {
// Set up the initial notification state.
- mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
- mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
+ mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback);
+ mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener);
mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
mStackScrollerController.setNotificationActivityStarter(
mNotificationActivityStarterLazy.get());
@@ -3176,8 +3176,8 @@
}
};
- private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
- new ActivityLaunchAnimator.Callback() {
+ private final ActivityTransitionAnimator.Callback mActivityTransitionAnimatorCallback =
+ new ActivityTransitionAnimator.Callback() {
@Override
public boolean isOnKeyguard() {
return mKeyguardStateController.isShowing();
@@ -3205,15 +3205,15 @@
}
};
- private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
- new ActivityLaunchAnimator.Listener() {
+ private final ActivityTransitionAnimator.Listener mActivityTransitionAnimatorListener =
+ new ActivityTransitionAnimator.Listener() {
@Override
- public void onLaunchAnimationStart() {
+ public void onTransitionAnimationStart() {
mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
}
@Override
- public void onLaunchAnimationEnd() {
+ public void onTransitionAnimationEnd() {
mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
}
};
@@ -3267,7 +3267,7 @@
}
@Override
- public ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification(
+ public ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification(
ExpandableNotificationRow associatedView) {
return mNotificationAnimationProvider.getAnimatorController(associatedView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index be5c6b3..8e3d678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -349,8 +349,12 @@
}
}
if (child instanceof StatusBarIconView) {
- ((StatusBarIconView) child).updateIconDimens();
- if (!NotificationIconContainerRefactor.isEnabled()) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ if (!mChangingViewPositions) {
+ ((StatusBarIconView) child).updateIconDimens();
+ }
+ } else {
+ ((StatusBarIconView) child).updateIconDimens();
((StatusBarIconView) child).setDozing(mDozing, false, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index d43f470..7ff5f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,7 +1,7 @@
package com.android.systemui.statusbar.phone
import android.view.View
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeViewController
@@ -9,17 +9,17 @@
import com.android.systemui.statusbar.NotificationShadeWindowController
/**
- * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
+ * A [ActivityTransitionAnimator.Controller] that takes care of collapsing the status bar at the right
* time.
*/
class StatusBarLaunchAnimatorController(
- private val delegate: ActivityLaunchAnimator.Controller,
+ private val delegate: ActivityTransitionAnimator.Controller,
private val shadeViewController: ShadeViewController,
private val shadeAnimationInteractor: ShadeAnimationInteractor,
private val shadeController: ShadeController,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val isLaunchForActivity: Boolean = true
-) : ActivityLaunchAnimator.Controller by delegate {
+) : ActivityTransitionAnimator.Controller by delegate {
// Always sync the opening window with the shade, given that we draw a hole punch in the shade
// of the same size and position as the opening app to make it visible.
override val openingWindowSyncView: View?
@@ -39,7 +39,7 @@
shadeAnimationInteractor.setIsLaunchingActivity(true)
if (!isExpandingFullyAbove) {
shadeViewController.collapseWithDuration(
- ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
+ ActivityTransitionAnimator.TIMINGS.totalDuration.toInt())
}
}
@@ -58,8 +58,8 @@
shadeViewController.applyLaunchAnimationProgress(linearProgress)
}
- override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
- delegate.onLaunchAnimationCancelled()
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ delegate.onTransitionAnimationCancelled()
shadeAnimationInteractor.setIsLaunchingActivity(false)
shadeController.onLaunchAnimationCancelled(isLaunchForActivity)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4ee061d..2737580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,7 +52,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -125,7 +125,7 @@
private final NotificationPresenter mPresenter;
private final ShadeViewController mShadeViewController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final ActivityTransitionAnimator mActivityTransitionAnimator;
private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
private final PowerInteractor mPowerInteractor;
private final UserTracker mUserTracker;
@@ -161,7 +161,7 @@
NotificationPresenter presenter,
ShadeViewController shadeViewController,
NotificationShadeWindowController notificationShadeWindowController,
- ActivityLaunchAnimator activityLaunchAnimator,
+ ActivityTransitionAnimator activityTransitionAnimator,
ShadeAnimationInteractor shadeAnimationInteractor,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
@@ -194,7 +194,7 @@
mOnUserInteractionCallback = onUserInteractionCallback;
mPresenter = presenter;
mShadeViewController = shadeViewController;
- mActivityLaunchAnimator = activityLaunchAnimator;
+ mActivityTransitionAnimator = activityTransitionAnimator;
mNotificationAnimationProvider = notificationAnimationProvider;
mPowerInteractor = powerInteractor;
mUserTracker = userTracker;
@@ -440,7 +440,7 @@
boolean isActivityIntent) {
mLogger.logStartNotificationIntent(entry);
try {
- ActivityLaunchAnimator.Controller animationController =
+ ActivityTransitionAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row, null),
mShadeViewController,
@@ -448,7 +448,7 @@
mShadeController,
mNotificationShadeWindowController,
isActivityIntent);
- mActivityLaunchAnimator.startPendingIntentWithAnimation(
+ mActivityTransitionAnimator.startPendingIntentWithAnimation(
animationController,
animate,
intent.getCreatorPackage(),
@@ -482,7 +482,7 @@
@Override
public boolean onDismiss() {
AsyncTask.execute(() -> {
- ActivityLaunchAnimator.Controller animationController =
+ ActivityTransitionAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row),
mShadeViewController,
@@ -491,7 +491,7 @@
mNotificationShadeWindowController,
true /* isActivityIntent */);
- mActivityLaunchAnimator.startIntentWithAnimation(
+ mActivityTransitionAnimator.startIntentWithAnimation(
animationController, animate, intent.getPackage(),
(adapter) -> TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
@@ -528,11 +528,11 @@
tsb.addNextIntent(intent);
}
- ActivityLaunchAnimator.Controller viewController =
- ActivityLaunchAnimator.Controller.fromView(view,
+ ActivityTransitionAnimator.Controller viewController =
+ ActivityTransitionAnimator.Controller.fromView(view,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
);
- ActivityLaunchAnimator.Controller animationController =
+ ActivityTransitionAnimator.Controller animationController =
viewController == null ? null
: new StatusBarLaunchAnimatorController(
viewController,
@@ -542,8 +542,8 @@
mNotificationShadeWindowController,
true /* isActivityIntent */);
- mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
- intent.getPackage(),
+ mActivityTransitionAnimator.startIntentWithAnimation(
+ animationController, animate, intent.getPackage(),
(adapter) -> tsb.startActivities(
getActivityOptions(mDisplayId, adapter),
mUserTracker.getUserHandle()));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
index 246645e..72f4540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt
@@ -15,20 +15,14 @@
*/
package com.android.systemui.statusbar.phone.domain.interactor
-import android.graphics.Rect
import com.android.systemui.statusbar.phone.data.repository.DarkIconRepository
+import com.android.systemui.statusbar.phone.domain.model.DarkState
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/** States pertaining to calculating colors for icons in dark mode. */
class DarkIconInteractor @Inject constructor(repository: DarkIconRepository) {
- /** @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.areas */
- val tintAreas: Flow<Collection<Rect>> = repository.darkState.map { it.areas }
- /**
- * @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.darkIntensity
- */
- val darkIntensity: Flow<Float> = repository.darkState.map { it.darkIntensity }
- /** @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.tint */
- val tintColor: Flow<Int> = repository.darkState.map { it.tint }
+ /** Dark-mode state for tinting icons. */
+ val darkState: Flow<DarkState> = repository.darkState.map { DarkState(it.areas, it.tint) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/model/DarkState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/model/DarkState.kt
new file mode 100644
index 0000000..3cab7cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/model/DarkState.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.statusbar.phone.domain.model
+
+import android.graphics.Rect
+
+/** Dark mode visual states. */
+data class DarkState(
+ /** Areas on screen that require a dark-mode adjustment. */
+ val areas: Collection<Rect>,
+ /** Tint color to apply to UI elements that fall within [areas]. */
+ val tint: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 0bdd1a5..a20468f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -30,7 +30,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -233,7 +233,7 @@
logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
intent,
- ActivityLaunchAnimator.Controller.fromView(
+ ActivityTransitionAnimator.Controller.fromView(
backgroundView,
InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/model/SatelliteIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/model/SatelliteIconModel.kt
index 63566ee..e1798d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/model/SatelliteIconModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/model/SatelliteIconModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.satellite.ui.model
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -36,7 +37,10 @@
SatelliteConnectionState.On ->
Icon.Resource(
res = R.drawable.ic_satellite_not_connected,
- contentDescription = null,
+ contentDescription =
+ ContentDescription.Resource(
+ R.string.accessibility_status_bar_satellite_available
+ ),
)
SatelliteConnectionState.Connected -> fromSignalStrength(signalStrength)
}
@@ -51,15 +55,36 @@
// TODO(b/316634365): these need content descriptions
when (signalStrength) {
// No signal
- 0 -> Icon.Resource(res = R.drawable.ic_satellite_connected_0, contentDescription = null)
+ 0 ->
+ Icon.Resource(
+ res = R.drawable.ic_satellite_connected_0,
+ contentDescription =
+ ContentDescription.Resource(
+ R.string.accessibility_status_bar_satellite_no_connection
+ )
+ )
// Poor -> Moderate
1,
- 2 -> Icon.Resource(res = R.drawable.ic_satellite_connected_1, contentDescription = null)
+ 2 ->
+ Icon.Resource(
+ res = R.drawable.ic_satellite_connected_1,
+ contentDescription =
+ ContentDescription.Resource(
+ R.string.accessibility_status_bar_satellite_poor_connection
+ )
+ )
// Good -> Great
3,
- 4 -> Icon.Resource(res = R.drawable.ic_satellite_connected_2, contentDescription = null)
+ 4 ->
+ Icon.Resource(
+ res = R.drawable.ic_satellite_connected_2,
+ contentDescription =
+ ContentDescription.Resource(
+ R.string.accessibility_status_bar_satellite_good_connection
+ )
+ )
else -> null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index 21d3fa4..65c2e20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -47,7 +47,7 @@
import android.view.WindowManager;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -188,8 +188,8 @@
* updated animation controller that handles status-bar-related animation details. Returns an
* empty optional if the animation is *not* on a view in the status bar.
*/
- public Optional<ActivityLaunchAnimator.Controller> wrapAnimationControllerIfInStatusBar(
- View rootView, ActivityLaunchAnimator.Controller animationController) {
+ public Optional<ActivityTransitionAnimator.Controller> wrapAnimationControllerIfInStatusBar(
+ View rootView, ActivityTransitionAnimator.Controller animationController) {
if (rootView != mStatusBarWindowView) {
return Optional.empty();
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index a5428470..139ac7e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.unfold
import com.android.keyguard.KeyguardUnfoldTransition
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
@@ -30,6 +31,8 @@
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
import java.util.Optional
import javax.inject.Named
@@ -73,6 +76,14 @@
}
@Module
+interface SysUIUnfoldStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(UnfoldInitializationStartable::class)
+ fun bindsUnfoldInitializationStartable(impl: UnfoldInitializationStartable): CoreStartable
+}
+
+@Module
abstract class SysUIUnfoldInternalModule {
@Binds
@IntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldInitializationStartable.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldInitializationStartable.kt
new file mode 100644
index 0000000..75d8a58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldInitializationStartable.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.unfold
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
+import com.android.systemui.unfold.dagger.UnfoldBg
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
+import java.util.Optional
+import javax.inject.Inject
+
+class UnfoldInitializationStartable
+@Inject
+constructor(
+ private val unfoldComponentOptional: Optional<SysUIUnfoldComponent>,
+ private val foldStateLoggingProviderOptional: Optional<FoldStateLoggingProvider>,
+ private val foldStateLoggerOptional: Optional<FoldStateLogger>,
+ @UnfoldBg
+ private val unfoldBgTransitionProgressProviderOptional:
+ Optional<UnfoldTransitionProgressProvider>,
+ private val unfoldTransitionProgressProviderOptional:
+ Optional<UnfoldTransitionProgressProvider>,
+ private val unfoldTransitionProgressForwarder: Optional<UnfoldTransitionProgressForwarder>
+) : CoreStartable {
+ override fun start() {
+ unfoldComponentOptional.ifPresent { c: SysUIUnfoldComponent ->
+ c.getFullScreenLightRevealAnimations().forEach { it: FullscreenLightRevealAnimation ->
+ it.init()
+ }
+ c.getUnfoldTransitionWallpaperController().init()
+ c.getUnfoldHapticsPlayer()
+ c.getNaturalRotationUnfoldProgressProvider().init()
+ c.getUnfoldLatencyTracker().init()
+ }
+
+ foldStateLoggingProviderOptional.ifPresent { obj: FoldStateLoggingProvider -> obj.init() }
+ foldStateLoggerOptional.ifPresent { obj: FoldStateLogger -> obj.init() }
+
+ val unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider> =
+ if (Flags.unfoldAnimationBackgroundProgress()) {
+ unfoldBgTransitionProgressProviderOptional
+ } else {
+ unfoldTransitionProgressProviderOptional
+ }
+ unfoldTransitionProgressProvider.ifPresent {
+ progressProvider: UnfoldTransitionProgressProvider ->
+ unfoldTransitionProgressForwarder.ifPresent {
+ listener: UnfoldTransitionProgressForwarder ->
+ progressProvider.addCallback(listener)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 0fb4b43..38b381a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -1,6 +1,7 @@
package com.android.systemui.user.domain.interactor
import android.annotation.UserIdInt
+import android.content.pm.UserInfo
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Flags.refactorGetCurrentUser
import com.android.systemui.dagger.SysUISingleton
@@ -16,6 +17,9 @@
/** Flow providing the ID of the currently selected user. */
val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged()
+ /** Flow providing the [UserInfo] of the currently selected user. */
+ val selectedUserInfo = repository.selectedUserInfo
+
/**
* Returns the ID of the currently-selected user.
*
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index c170eb5..a122311 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -27,7 +27,6 @@
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
-import android.os.Process
import android.os.RemoteException
import android.os.UserHandle
import android.os.UserManager
@@ -50,6 +49,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.process.ProcessWrapper
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.res.R
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -108,6 +108,7 @@
private val guestUserInteractor: GuestUserInteractor,
private val uiEventLogger: UiEventLogger,
private val userRestrictionChecker: UserRestrictionChecker,
+ private val processWrapper: ProcessWrapper
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -669,7 +670,7 @@
// Connect to the new secondary user's service (purely to ensure that a persistent
// SystemUI application is created for that user)
- if (userId != Process.myUserHandle().identifier) {
+ if (userId != processWrapper.myUserHandle().identifier && !processWrapper.isSystemUser) {
applicationContext.startServiceAsUser(
intent,
UserHandle.of(userId),
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt b/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt
deleted file mode 100644
index d3653b4..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/view/DisposableHandleExt.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util.view
-
-import android.view.View
-import com.android.systemui.util.kotlin.awaitCancellationThenDispose
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Use the [bind] method to bind the view every time this flow emits, and suspend to await for more
- * updates. New emissions lead to the previous binding call being cancelled if not completed.
- * Dispose of the [DisposableHandle] returned by [bind] when done.
- */
-suspend fun <T : View> Flow<T>.bindLatest(bind: (T) -> DisposableHandle?) {
- this.collectLatest { view ->
- val disposableHandle = bind(view)
- disposableHandle?.awaitCancellationThenDispose()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index ff1daea..1af5c46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -18,9 +18,14 @@
import android.content.Context
import android.media.AudioManager
+import com.android.settingslib.media.data.repository.SpatializerRepository
+import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
+import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiverImpl
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -35,16 +40,33 @@
companion object {
@Provides
- fun provideAudioRepository(
+ fun provideAudioManagerIntentsReceiver(
@Application context: Context,
+ @Application coroutineScope: CoroutineScope,
+ ): AudioManagerIntentsReceiver = AudioManagerIntentsReceiverImpl(context, coroutineScope)
+
+ @Provides
+ fun provideAudioRepository(
+ intentsReceiver: AudioManagerIntentsReceiver,
audioManager: AudioManager,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
): AudioRepository =
- AudioRepositoryImpl(context, audioManager, coroutineContext, coroutineScope)
+ AudioRepositoryImpl(intentsReceiver, audioManager, coroutineContext, coroutineScope)
@Provides
fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor =
AudioModeInteractor(repository)
+
+ @Provides
+ fun provdieSpatializerRepository(
+ audioManager: AudioManager,
+ @Background backgroundContext: CoroutineContext,
+ ): SpatializerRepository =
+ SpatializerRepositoryImpl(audioManager.spatializer, backgroundContext)
+
+ @Provides
+ fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
+ SpatializerInteractor(repository)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 2ff9af9..ab76d45 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -16,11 +16,11 @@
package com.android.systemui.volume.dagger
-import android.content.Context
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -37,14 +37,14 @@
@Provides
@SysUISingleton
fun provideMediaDeviceSessionRepository(
- @Application context: Context,
+ intentsReceiver: AudioManagerIntentsReceiver,
mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
@Background backgroundContext: CoroutineContext,
): MediaControllerRepository =
MediaControllerRepositoryImpl(
- context,
+ intentsReceiver,
mediaSessionManager,
localBluetoothManager,
coroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index 57ac435..0a1ee24 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -15,8 +15,10 @@
*/
package com.android.systemui.volume.panel.component.mediaoutput.data.repository
+import android.media.MediaRouter2Manager
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
+import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.media.controls.pipeline.LocalMediaManagerFactory
@@ -27,6 +29,8 @@
class LocalMediaRepositoryFactory
@Inject
constructor(
+ private val intentsReceiver: AudioManagerIntentsReceiver,
+ private val mediaRouter2Manager: MediaRouter2Manager,
private val localMediaManagerFactory: LocalMediaManagerFactory,
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
@@ -34,7 +38,9 @@
fun create(packageName: String?): LocalMediaRepository =
LocalMediaRepositoryImpl(
+ intentsReceiver,
localMediaManagerFactory.create(packageName),
+ mediaRouter2Manager,
coroutineScope,
backgroundCoroutineContext,
)
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index e0228d9..1d9b90a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -34,7 +34,7 @@
import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
import android.util.Log;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -236,12 +236,12 @@
* that too is null, then fall back to {@link WalletActivity}.
*
* @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent.
- * @param animationController an {@link ActivityLaunchAnimator.Controller} to provide a
+ * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a
* smooth animation for the activity launch.
* @param hasCard whether the service returns any cards.
*/
public void startQuickAccessUiIntent(ActivityStarter activityStarter,
- ActivityLaunchAnimator.Controller animationController,
+ ActivityTransitionAnimator.Controller animationController,
boolean hasCard) {
mQuickAccessWalletClient.getWalletPendingIntent(mExecutor,
walletPendingIntent -> {
@@ -271,7 +271,7 @@
private void startQuickAccessViaIntent(Intent intent,
boolean hasCard,
ActivityStarter activityStarter,
- ActivityLaunchAnimator.Controller animationController) {
+ ActivityTransitionAnimator.Controller animationController) {
if (hasCard) {
activityStarter.startActivity(intent, true /* dismissShade */,
animationController, true /* showOverLockscreenWhenLocked */);
@@ -285,7 +285,7 @@
private void startQuickAccessViaPendingIntent(PendingIntent pendingIntent,
ActivityStarter activityStarter,
- ActivityLaunchAnimator.Controller animationController) {
+ ActivityTransitionAnimator.Controller animationController) {
activityStarter.postStartActivityDismissingKeyguard(
pendingIntent,
animationController);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index c2efc05..d048cbe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -28,6 +28,7 @@
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardPinViewController.PinBouncerUiEvent
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -88,6 +89,7 @@
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
+ private val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
@Mock lateinit var postureController: DevicePostureController
@Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -143,7 +145,7 @@
featureFlags,
mSelectedUserInteractor,
uiEventLogger,
- FakeKeyboardRepository()
+ keyguardKeyboardInteractor
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 0959f1b..4a2554e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -23,6 +23,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -80,6 +81,7 @@
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
as KeyguardSimPinView
val fakeFeatureFlags = FakeFeatureFlags()
+ val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
underTest =
@@ -97,7 +99,7 @@
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- FakeKeyboardRepository()
+ keyguardKeyboardInteractor
)
underTest.init()
underTest.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 1281e44..4f46184 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -75,6 +76,7 @@
simPukView =
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
as KeyguardSimPukView
+ val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
val fakeFeatureFlags = FakeFeatureFlags()
mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
underTest =
@@ -92,7 +94,7 @@
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- FakeKeyboardRepository()
+ keyguardKeyboardInteractor
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
index 202d9ce..e157fc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
@@ -91,7 +91,7 @@
whenever(sysuiComponent.startables)
.thenReturn(mutableMapOf(StartableA::class.java to Provider { startableA }))
app.onCreate()
- app.startServicesIfNeeded()
+ app.startSystemUserServicesIfNeeded()
assertThat(startableA.started).isTrue()
}
@@ -105,7 +105,7 @@
)
)
app.onCreate()
- app.startServicesIfNeeded()
+ app.startSystemUserServicesIfNeeded()
assertThat(startableA.started).isTrue()
assertThat(startableB.started).isTrue()
}
@@ -121,7 +121,7 @@
)
)
app.onCreate()
- app.startServicesIfNeeded()
+ app.startSystemUserServicesIfNeeded()
assertThat(startableA.started).isTrue()
assertThat(startableB.started).isTrue()
assertThat(startableC.started).isTrue()
@@ -141,7 +141,7 @@
)
)
app.onCreate()
- app.startServicesIfNeeded()
+ app.startSystemUserServicesIfNeeded()
assertThat(startableA.started).isTrue()
assertThat(startableB.started).isTrue()
assertThat(startableC.started).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 375ebe8..8299acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -51,7 +51,6 @@
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -80,7 +79,6 @@
@LargeTest
@RunWith(AndroidTestingRunner.class)
-@FlakyTest(bugId = 308501761)
public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
similarity index 79%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 722107c..75a49d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -44,37 +44,37 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
-class ActivityLaunchAnimatorTest : SysuiTestCase() {
+class ActivityTransitionAnimatorTest : SysuiTestCase() {
private val transitionContainer = LinearLayout(mContext)
private val testTransitionAnimator = fakeTransitionAnimator()
- @Mock lateinit var callback: ActivityLaunchAnimator.Callback
- @Mock lateinit var listener: ActivityLaunchAnimator.Listener
- @Spy private val controller = TestLaunchAnimatorController(transitionContainer)
+ @Mock lateinit var callback: ActivityTransitionAnimator.Callback
+ @Mock lateinit var listener: ActivityTransitionAnimator.Listener
+ @Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
- private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+ private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setup() {
- activityLaunchAnimator =
- ActivityLaunchAnimator(
+ activityTransitionAnimator =
+ ActivityTransitionAnimator(
testTransitionAnimator,
testTransitionAnimator,
disableWmTimeout = true
)
- activityLaunchAnimator.callback = callback
- activityLaunchAnimator.addListener(listener)
+ activityTransitionAnimator.callback = callback
+ activityTransitionAnimator.addListener(listener)
}
@After
fun tearDown() {
- activityLaunchAnimator.removeListener(listener)
+ activityTransitionAnimator.removeListener(listener)
}
private fun startIntentWithAnimation(
- animator: ActivityLaunchAnimator = this.activityLaunchAnimator,
- controller: ActivityLaunchAnimator.Controller? = this.controller,
+ animator: ActivityTransitionAnimator = this.activityTransitionAnimator,
+ controller: ActivityTransitionAnimator.Controller? = this.controller,
animate: Boolean = true,
intentStarter: (RemoteAnimationAdapter?) -> Int
) {
@@ -138,7 +138,7 @@
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
var animationAdapter: RemoteAnimationAdapter? = null
- startIntentWithAnimation(activityLaunchAnimator) { adapter ->
+ startIntentWithAnimation(activityTransitionAnimator) { adapter ->
animationAdapter = adapter
ActivityManager.START_DELIVERED_TO_TOP
}
@@ -163,50 +163,50 @@
@Test
fun doesNotStartIfAnimationIsCancelled() {
- val runner = activityLaunchAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createRunner(controller)
runner.onAnimationCancelled()
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(controller).onLaunchAnimationCancelled()
+ verify(controller).onTransitionAnimationCancelled()
verify(controller, never()).onTransitionAnimationStart(anyBoolean())
- verify(listener).onLaunchAnimationCancelled()
- verify(listener, never()).onLaunchAnimationStart()
+ verify(listener).onTransitionAnimationCancelled()
+ verify(listener, never()).onTransitionAnimationStart()
assertNull(runner.delegate)
}
@Test
fun cancelsIfNoOpeningWindowIsFound() {
- val runner = activityLaunchAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createRunner(controller)
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(controller).onLaunchAnimationCancelled()
+ verify(controller).onTransitionAnimationCancelled()
verify(controller, never()).onTransitionAnimationStart(anyBoolean())
- verify(listener).onLaunchAnimationCancelled()
- verify(listener, never()).onLaunchAnimationStart()
+ verify(listener).onTransitionAnimationCancelled()
+ verify(listener, never()).onTransitionAnimationStart()
assertNull(runner.delegate)
}
@Test
fun startsAnimationIfWindowIsOpening() {
- val runner = activityLaunchAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createRunner(controller)
runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(listener).onLaunchAnimationStart()
+ verify(listener).onTransitionAnimationStart()
verify(controller).onTransitionAnimationStart(anyBoolean())
}
@Test
fun creatingControllerFromNormalViewThrows() {
assertThrows(IllegalArgumentException::class.java) {
- ActivityLaunchAnimator.Controller.fromView(FrameLayout(mContext))
+ ActivityTransitionAnimator.Controller.fromView(FrameLayout(mContext))
}
}
@Test
fun disposeRunner_delegateDereferenced() {
- val runner = activityLaunchAnimator.createRunner(controller)
+ val runner = activityTransitionAnimator.createRunner(controller)
assertNotNull(runner.delegate)
runner.dispose()
waitForIdleSync()
@@ -241,11 +241,11 @@
}
/**
- * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
+ * A simple implementation of [ActivityTransitionAnimator.Controller] which throws if it is called
* outside of the main thread.
*/
-private class TestLaunchAnimatorController(override var transitionContainer: ViewGroup) :
- ActivityLaunchAnimator.Controller {
+private class TestTransitionAnimatorController(override var transitionContainer: ViewGroup) :
+ ActivityTransitionAnimator.Controller {
override fun createAnimatorState() =
TransitionAnimator.State(
top = 100,
@@ -282,7 +282,7 @@
assertOnMainThread()
}
- override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
assertOnMainThread()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
deleted file mode 100644
index 112cec2..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- *
- */
-
-package com.android.systemui.common.ui
-
-import android.content.Context
-import android.testing.AndroidTestingRunner
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.mockito.captureMany
-import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.verify
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class ConfigurationStateTest : SysuiTestCase() {
-
- private val configurationController: ConfigurationController = mock()
- private val layoutInflater = TestLayoutInflater()
- private val backgroundDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(backgroundDispatcher)
-
- val underTest = ConfigurationState(configurationController, context, layoutInflater)
-
- @Test
- fun reinflateAndBindLatest_inflatesWithoutEmission() =
- testScope.runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- backgroundDispatcher,
- ) {
- callbackCount++
- null
- }
- }
-
- // Inflates without an emission
- runCurrent()
- assertThat(layoutInflater.inflationCount).isEqualTo(1)
- assertThat(callbackCount).isEqualTo(1)
- }
-
- @Test
- fun reinflateAndBindLatest_reinflatesOnThemeChanged() =
- testScope.runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- backgroundDispatcher,
- ) {
- callbackCount++
- null
- }
- }
- runCurrent()
-
- val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
- verify(configurationController, atLeastOnce()).addCallback(capture())
- }
-
- listOf(1, 2, 3).forEach { count ->
- assertThat(layoutInflater.inflationCount).isEqualTo(count)
- assertThat(callbackCount).isEqualTo(count)
- configListeners.forEach { it.onThemeChanged() }
- runCurrent()
- }
- }
-
- @Test
- fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() =
- testScope.runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- backgroundDispatcher,
- ) {
- callbackCount++
- null
- }
- }
- runCurrent()
-
- val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
- verify(configurationController, atLeastOnce()).addCallback(capture())
- }
-
- listOf(1, 2, 3).forEach { count ->
- assertThat(layoutInflater.inflationCount).isEqualTo(count)
- assertThat(callbackCount).isEqualTo(count)
- configListeners.forEach { it.onDensityOrFontScaleChanged() }
- runCurrent()
- }
- }
-
- @Test
- fun testReinflateAndBindLatest_disposesOnCancel() =
- testScope.runTest {
- var callbackCount = 0
- var disposed = false
- val job = launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- backgroundDispatcher,
- ) {
- callbackCount++
- DisposableHandle { disposed = true }
- }
- }
-
- runCurrent()
- job.cancelAndJoin()
- assertThat(disposed).isTrue()
- }
-
- inner class TestLayoutInflater : LayoutInflater(context) {
-
- var inflationCount = 0
-
- override fun inflate(resource: Int, root: ViewGroup?, attachToRoot: Boolean): View {
- inflationCount++
- return View(context)
- }
-
- override fun cloneInContext(p0: Context?): LayoutInflater {
- // not needed for this test
- return this
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index b28d0c8..e30dd35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -33,8 +33,7 @@
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
-import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.data.repository.fakeFaceWakeUpTriggersConfig
import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -56,10 +55,9 @@
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -68,37 +66,33 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
- val kosmos =
- testKosmos().apply { this.faceWakeUpTriggersConfig = mock<FaceWakeUpTriggersConfig>() }
+ private val kosmos = testKosmos()
+ private val testScope: TestScope = kosmos.testScope
private lateinit var underTest: SystemUIDeviceEntryFaceAuthInteractor
- private val testScope = kosmos.testScope
private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
private val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
private val fakeUserRepository = kosmos.fakeUserRepository
private val facePropertyRepository = kosmos.facePropertyRepository
- private val fakeDeviceEntryFingerprintAuthRepository =
- kosmos.fakeDeviceEntryFingerprintAuthRepository
+ private val fakeDeviceEntryFingerprintAuthInteractor =
+ kosmos.deviceEntryFingerprintAuthInteractor
private val powerInteractor = kosmos.powerInteractor
private val fakeBiometricSettingsRepository = kosmos.fakeBiometricSettingsRepository
private val keyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
- private val faceWakeUpTriggersConfig = kosmos.faceWakeUpTriggersConfig
+ private val faceWakeUpTriggersConfig = kosmos.fakeFaceWakeUpTriggersConfig
private val trustManager = kosmos.trustManager
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
-
underTest =
SystemUIDeviceEntryFaceAuthInteractor(
mContext,
@@ -110,7 +104,7 @@
keyguardTransitionInteractor,
FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")),
keyguardUpdateMonitor,
- fakeDeviceEntryFingerprintAuthRepository,
+ fakeDeviceEntryFingerprintAuthInteractor,
fakeUserRepository,
facePropertyRepository,
faceWakeUpTriggersConfig,
@@ -126,10 +120,9 @@
underTest.start()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
- whenever(
- faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
- )
- .thenReturn(true)
+ faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
+ setOf(WakeSleepReason.LID.powerManagerWakeReason)
+ )
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -168,10 +161,9 @@
underTest.start()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
- whenever(
- faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
- )
- .thenReturn(true)
+ faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
+ setOf(WakeSleepReason.LID.powerManagerWakeReason)
+ )
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -194,10 +186,9 @@
underTest.start()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LIFT)
- whenever(
- faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LIFT)
- )
- .thenReturn(false)
+ faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
+ setOf(WakeSleepReason.LID.powerManagerWakeReason)
+ )
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -217,10 +208,9 @@
underTest.start()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LID)
- whenever(
- faceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(WakeSleepReason.LID)
- )
- .thenReturn(true)
+ faceWakeUpTriggersConfig.setTriggerFaceAuthOnWakeUpFrom(
+ setOf(WakeSleepReason.LID.powerManagerWakeReason)
+ )
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
@@ -440,7 +430,45 @@
underTest.start()
fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
- fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(faceAuthRepository.isLockedOut.value).isTrue()
+ }
+
+ @Test
+ fun faceLockoutStateIsResetWheneverFingerprintIsNotLockedOut() =
+ testScope.runTest {
+ underTest.start()
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
+ fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(faceAuthRepository.isLockedOut.value).isTrue()
+ facePropertyRepository.setLockoutMode(primaryUserId, LockoutMode.NONE)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+ runCurrent()
+
+ assertThat(faceAuthRepository.isLockedOut.value).isFalse()
+ }
+
+ @Test
+ fun faceLockoutStateIsSetToUsersLockoutStateWheneverFingerprintIsNotLockedOut() =
+ testScope.runTest {
+ underTest.start()
+ fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
+ fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(true)
+ runCurrent()
+
+ assertThat(faceAuthRepository.isLockedOut.value).isTrue()
+ facePropertyRepository.setLockoutMode(primaryUserId, LockoutMode.TIMED)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
runCurrent()
assertThat(faceAuthRepository.isLockedOut.value).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 24cf164..2732047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -86,7 +86,7 @@
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -186,7 +186,7 @@
private @Mock ShadeController mShadeController;
private NotificationShadeWindowController mNotificationShadeWindowController;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
- private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+ private @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
private @Mock ScrimController mScrimController;
private @Mock IActivityTaskManager mActivityTaskManagerService;
private @Mock SysuiColorExtractor mColorExtractor;
@@ -763,7 +763,7 @@
@Test
public void testUpdateIsKeyguardAfterOccludeAnimationIsCancelled() {
- mViewMediator.mOccludeAnimationController.onLaunchAnimationCancelled(
+ mViewMediator.mOccludeAnimationController.onTransitionAnimationCancelled(
null /* newKeyguardOccludedState */);
// Since the updateIsKeyguard call is delayed during the animation, ensure it's called if
@@ -1231,7 +1231,7 @@
mWallpaperRepository,
() -> mShadeController,
() -> mNotificationShadeWindowController,
- () -> mActivityLaunchAnimator,
+ () -> mActivityTransitionAnimator,
() -> mScrimController,
mActivityTaskManagerService,
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index b4ae7e3..798c7f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
@@ -225,7 +225,7 @@
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
+ @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
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 2d9d5ed..0e9197e 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
@@ -181,6 +181,31 @@
}
@Test
+ fun usesOnStepToDoubleValueWithState() =
+ testScope.runTest {
+ val flow =
+ underTest.sharedFlowWithState(
+ duration = 1000.milliseconds,
+ onStep = { it * 2 },
+ )
+ val animationValues by collectLastValue(flow)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.STARTED, 0f))
+ repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 0.6f))
+ repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 1.2f))
+ repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 1.6f))
+ repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 2f))
+ repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(animationValues).isEqualTo(StateToValue(TransitionState.FINISHED, null))
+ }
+
+ @Test
fun sameFloatValueWithTheSameTransitionStateDoesNotEmitTwice() =
testScope.runTest {
val flow =
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 1c9c942..bfa8433 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
@@ -26,6 +26,7 @@
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.StateToValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.collect.Range
@@ -57,17 +58,19 @@
// The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- assertThat(enterFromTopTranslationY).isEqualTo(pixels)
+ assertThat(enterFromTopTranslationY)
+ .isEqualTo(StateToValue(TransitionState.STARTED, pixels))
- repository.sendTransitionStep(step(0.4f))
- assertThat(enterFromTopTranslationY).isEqualTo(pixels)
+ repository.sendTransitionStep(step(.55f))
+ assertThat(enterFromTopTranslationY!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
repository.sendTransitionStep(step(.85f))
- assertThat(enterFromTopTranslationY).isIn(Range.closed(pixels, 0f))
+ assertThat(enterFromTopTranslationY!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
// At the end, the translation should be complete and set to zero
repository.sendTransitionStep(step(1f))
- assertThat(enterFromTopTranslationY).isEqualTo(0f)
+ assertThat(enterFromTopTranslationY)
+ .isEqualTo(StateToValue(TransitionState.RUNNING, 0f))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index c6cfabc..32b6f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -72,7 +72,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -110,7 +110,7 @@
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
@Mock
- private ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController;
+ private ActivityTransitionAnimator.Controller mActivityTransitionAnimatorController;
@Mock
private NearbyMediaDevicesManager mNearbyMediaDevicesManager;
// Mock
@@ -143,7 +143,7 @@
@Mock
private KeyguardManager mKeyguardManager;
@Mock
- private ActivityLaunchAnimator.Controller mController;
+ private ActivityTransitionAnimator.Controller mController;
@Mock
private PowerExemptionManager mPowerExemptionManager;
@Mock
@@ -1122,7 +1122,7 @@
@Test
public void launchBluetoothPairing_isKeyguardLocked_dismissDialog() {
when(mDialogLaunchAnimator.createActivityLaunchController(mDialogLaunchView)).thenReturn(
- mActivityLaunchAnimatorController);
+ mActivityTransitionAnimatorController);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
mMediaOutputController.mCallback = this.mCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
index 301d887..d9453d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
@@ -33,6 +33,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
manager = NearbyMediaDevicesManager(commandQueue, logger)
+ manager.start()
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index ae47a7b..33f8f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -38,7 +38,7 @@
import android.view.View
import com.android.internal.logging.MetricsLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.view.LaunchableFrameLayout
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -372,7 +372,7 @@
verify(activityStarter)
.startPendingIntentMaybeDismissingKeyguard(
- eq(pi), nullable(), nullable<ActivityLaunchAnimator.Controller>())
+ eq(pi), nullable(), nullable<ActivityTransitionAnimator.Controller>())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 23466cc..720c25a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -26,7 +26,7 @@
import com.android.internal.logging.testing.FakeMetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
@@ -127,7 +127,7 @@
.startActivity(
intentCaptor.capture(),
/* dismissShade= */ eq(true),
- nullable() as? ActivityLaunchAnimator.Controller,
+ nullable() as? ActivityTransitionAnimator.Controller,
)
assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SETTINGS)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 3bf59ca..874368b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -29,7 +29,7 @@
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlInfo
@@ -348,7 +348,7 @@
verify(activityStarter).startActivity(
intentCaptor.capture(),
eq(true) /* dismissShade */,
- nullable(ActivityLaunchAnimator.Controller::class.java),
+ nullable(ActivityTransitionAnimator.Controller::class.java),
eq(true) /* showOverLockscreenWhenLocked */)
assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
}
@@ -379,7 +379,7 @@
verify(activityStarter).startActivity(
intentCaptor.capture(),
anyBoolean() /* dismissShade */,
- nullable(ActivityLaunchAnimator.Controller::class.java),
+ nullable(ActivityTransitionAnimator.Controller::class.java),
eq(false) /* showOverLockscreenWhenLocked */)
assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 9517f82..1dc5f7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -103,8 +103,6 @@
)
testableLooper = TestableLooper.get(this)
- communalRepository.setIsCommunalEnabled(true)
-
whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any()))
.thenReturn(bouncerShowingFlow)
whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow)
@@ -125,36 +123,6 @@
}
@Test
- fun isEnabled_communalEnabled_returnsTrue() {
- communalRepository.setIsCommunalEnabled(true)
-
- assertThat(underTest.isEnabled()).isTrue()
- }
-
- @Test
- fun isEnabled_communalDisabled_returnsFalse() {
- communalRepository.setIsCommunalEnabled(false)
-
- assertThat(underTest.isEnabled()).isFalse()
- }
-
- @Test
- fun initView_notEnabled_throwsException() {
- communalRepository.setIsCommunalEnabled(false)
-
- underTest =
- GlanceableHubContainerController(
- communalInteractor,
- communalViewModel,
- keyguardTransitionInteractor,
- shadeInteractor,
- powerManager,
- )
-
- assertThrows(RuntimeException::class.java) { underTest.initView(context) }
- }
-
- @Test
fun initView_calledTwice_throwsException() {
underTest =
GlanceableHubContainerController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 22b05be..248ed24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -488,7 +488,8 @@
return
}
- whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true)
+ whenever(mGlanceableHubContainerController.communalAvailable())
+ .thenReturn(MutableStateFlow(true))
val mockCommunalView = mock(View::class.java)
whenever(mGlanceableHubContainerController.initView(any<Context>()))
@@ -513,7 +514,6 @@
return
}
- whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false)
whenever(mGlanceableHubContainerController.communalAvailable())
.thenReturn(MutableStateFlow(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index f58ff0a..3315e68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -97,7 +97,7 @@
@Test
fun testHunIsRemovedAndCallbackIsInvokedWhenAnimationIsCancelled() {
flagNotificationAsHun()
- controller.onLaunchAnimationCancelled()
+ controller.onTransitionAnimationCancelled()
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 7589a49..354f3f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -797,6 +797,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_remoteInput() {
ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
ArgumentCaptor.forClass(RemoteInputController.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4afcc8c..04f3216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -440,6 +440,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_noNotifications() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -451,6 +452,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_remoteInput() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -467,6 +469,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_withoutNotifications() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -482,6 +485,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -497,6 +501,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_withoutHistory() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -513,6 +518,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(false);
@@ -528,6 +534,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateFooter_oneNonClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(true);
@@ -544,9 +551,7 @@
}
@Test
- public void testUpdateFooter_atEnd() {
- mStackScroller.setCurrentUserSetup(true);
-
+ public void testFooterPosition_atEnd() {
// add footer
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -559,8 +564,6 @@
// Expecting the footer to be the last child
int expected = mStackScroller.getChildCount() - 1;
-
- // move footer to end
verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(expected));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 5a57035..3d90cd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -41,6 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
class StackStateAnimatorTest : SysuiTestCase() {
private lateinit var stackStateAnimator: StackStateAnimator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 88e4f5a..3a7659d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -27,19 +27,27 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -58,11 +66,16 @@
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
private val testScope = kosmos.testScope
+
private val activeNotificationListRepository = kosmos.activeNotificationListRepository
- private val fakeKeyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val fakeShadeRepository = kosmos.fakeShadeRepository
- private val zenModeRepository = kosmos.zenModeRepository
private val fakeConfigurationController = kosmos.fakeConfigurationController
+ private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository
+ private val fakeKeyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val fakePowerRepository = kosmos.fakePowerRepository
+ private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
+ private val fakeShadeRepository = kosmos.fakeShadeRepository
+ private val fakeUserSetupRepository = kosmos.fakeUserSetupRepository
+ private val zenModeRepository = kosmos.zenModeRepository
val underTest = kosmos.notificationListViewModel
@@ -273,4 +286,193 @@
assertThat(hasFilteredNotifs).isFalse()
}
+
+ @Test
+ fun testShouldShowFooterView_trueWhenShade() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer is visible
+ assertThat(shouldShow?.value).isTrue()
+ }
+
+ @Test
+ fun testShouldShowFooterView_trueWhenLockedShade() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open on lockscreen
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer is visible
+ assertThat(shouldShow?.value).isTrue()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenKeyguard() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND is on keyguard
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenUserNotSetUp() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ // AND user is not set up
+ fakeUserSetupRepository.setUserSetUp(false)
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenStartingToSleep() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ // AND device is starting to go to sleep
+ fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenQsExpandedDefault() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ // AND quick settings are expanded
+ fakeShadeRepository.setQsExpansion(1f)
+ fakeShadeRepository.legacyQsFullscreen.value = true
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_trueWhenQsExpandedSplitShade() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ // AND quick settings are expanded
+ fakeShadeRepository.setQsExpansion(1f)
+ // AND split shade is enabled
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ fakeConfigurationController.notifyConfigurationChanged()
+ runCurrent()
+
+ // THEN footer is visible
+ assertThat(shouldShow?.value).isTrue()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenRemoteInputActive() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ // AND remote input is active
+ fakeRemoteInputRepository.isRemoteInputActive.value = true
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_falseWhenShadeIsClosed() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is closed
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(0f)
+ runCurrent()
+
+ // THEN footer is not visible
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ fun testShouldShowFooterView_animatesWhenShade() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND shade is open and fully expanded
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer visibility animates
+ assertThat(shouldShow?.isAnimating).isTrue()
+ }
+
+ @Test
+ fun testShouldShowFooterView_notAnimatingOnKeyguard() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN has notifs
+ activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND we are on the keyguard
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ fakeShadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ // THEN footer visibility does not animate
+ assertThat(shouldShow?.isAnimating).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index b048949..9c60a2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -83,7 +83,7 @@
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.biometrics.AuthRippleController;
@@ -307,7 +307,7 @@
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
+ @Mock private ActivityTransitionAnimator mActivityTransitionAnimator;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@@ -543,7 +543,7 @@
new MessageRouterImpl(mMainExecutor),
mWallpaperManager,
Optional.of(mStartingSurface),
- mActivityLaunchAnimator,
+ mActivityTransitionAnimator,
mDeviceStateManager,
mWiredChargingRippleController,
mDreamManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 597e2e3..41514ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -63,7 +63,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -159,7 +159,7 @@
@Mock
private StatusBarNotificationActivityStarter mNotificationActivityStarter;
@Mock
- private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private ActivityTransitionAnimator mActivityTransitionAnimator;
@Mock
private InteractionJankMonitor mJankMonitor;
private FakePowerRepository mPowerRepository;
@@ -255,7 +255,7 @@
mock(NotificationPresenter.class),
mock(ShadeViewController.class),
mock(NotificationShadeWindowController.class),
- mActivityLaunchAnimator,
+ mActivityTransitionAnimator,
new ShadeAnimationInteractorLegacyImpl(
new ShadeAnimationRepository(), new FakeShadeRepository()),
notificationAnimationProvider,
@@ -306,7 +306,7 @@
// Then
verify(mShadeController, atLeastOnce()).collapseShade();
- verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
+ verify(mActivityTransitionAnimator).startPendingIntentWithAnimation(any(),
eq(false) /* animate */, any(), any());
verify(mAssistManager).hideAssist();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index b6a033a..1b43851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.process.processWrapper
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -1147,6 +1148,7 @@
uiEventLogger = uiEventLogger,
featureFlags = kosmos.fakeFeatureFlagsClassic,
userRestrictionChecker = mock(),
+ processWrapper = kosmos.processWrapper,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 21d4549..661837b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.process.ProcessWrapperFake
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -264,6 +265,7 @@
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
userRestrictionChecker = mock(),
+ processWrapper = ProcessWrapperFake()
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index d0804be..5661e20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.process.ProcessWrapperFake
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -176,6 +177,7 @@
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
userRestrictionChecker = mock(),
+ processWrapper = ProcessWrapperFake()
),
guestUserInteractor = guestUserInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 8263174..fccb936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -37,10 +37,10 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -68,7 +68,7 @@
@Mock
private ActivityStarter mActivityStarter;
@Mock
- private ActivityLaunchAnimator.Controller mAnimationController;
+ private ActivityTransitionAnimator.Controller mAnimationController;
@Captor
private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
@Captor
@@ -219,7 +219,7 @@
public void getQuickAccessUiIntent_hasCards_noPendingIntent_startsWalletActivity() {
mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true);
verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true),
- any(ActivityLaunchAnimator.Controller.class), eq(true));
+ any(ActivityTransitionAnimator.Controller.class), eq(true));
Intent intent = mIntentCaptor.getValue();
assertEquals(intent.getAction(), Intent.ACTION_VIEW);
assertEquals(
@@ -231,7 +231,7 @@
public void getQuickAccessUiIntent_noCards_noPendingIntent_startsWalletActivity() {
mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, false);
verify(mActivityStarter).postStartActivityDismissingKeyguard(mIntentCaptor.capture(), eq(0),
- any(ActivityLaunchAnimator.Controller.class));
+ any(ActivityTransitionAnimator.Controller.class));
Intent intent = mIntentCaptor.getValue();
assertEquals(intent.getAction(), Intent.ACTION_VIEW);
assertEquals(
@@ -254,7 +254,7 @@
}).when(mQuickAccessWalletClient).getWalletPendingIntent(any(), any());
mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true);
verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntentCaptor.capture(),
- any(ActivityLaunchAnimator.Controller.class));
+ any(ActivityTransitionAnimator.Controller.class));
PendingIntent pendingIntent = mPendingIntentCaptor.getValue();
Intent intent = pendingIntent.getIntent();
assertEquals(intent.getAction(), Intent.ACTION_VIEW);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
similarity index 88%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
index 128f58b..66c9afb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
@@ -18,4 +18,4 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+val Kosmos.activityTransitionAnimator by Kosmos.Fixture { ActivityTransitionAnimator() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
new file mode 100644
index 0000000..5485f79
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.data.repository
+
+import android.app.admin.devicePolicyManager
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.settings.fakeSettings
+
+val Kosmos.communalSettingsRepository: CommunalSettingsRepository by
+ Kosmos.Fixture {
+ CommunalSettingsRepositoryImpl(
+ bgDispatcher = testDispatcher,
+ featureFlagsClassic = featureFlagsClassic,
+ secureSettings = fakeSettings,
+ broadcastDispatcher = broadcastDispatcher,
+ devicePolicyManager = devicePolicyManager,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index cccd908..ae7d877 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -16,7 +16,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
class FakeCommunalRepository(
applicationScope: CoroutineScope,
- override var isCommunalEnabled: Boolean = true,
override val desiredScene: MutableStateFlow<CommunalSceneKey> =
MutableStateFlow(CommunalSceneKey.DEFAULT),
) : CommunalRepository {
@@ -40,21 +39,10 @@
_transitionState.value = transitionState
}
- fun setIsCommunalEnabled(value: Boolean) {
- isCommunalEnabled = value
- }
-
private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
_isCommunalHubShowing.value = isCommunalHubShowing
}
-
- private val _communalEnabledState: MutableStateFlow<Boolean> = MutableStateFlow(false)
- override val communalEnabledState: StateFlow<Boolean> = _communalEnabledState
-
- fun setCommunalEnabledState(enabled: Boolean) {
- _communalEnabledState.value = enabled
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index c47f020..f7e9a11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -27,7 +27,6 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.smartspace.data.repository.smartspaceRepository
-import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.util.mockito.mock
val Kosmos.communalInteractor by Fixture {
@@ -38,12 +37,12 @@
mediaRepository = communalMediaRepository,
communalPrefsRepository = communalPrefsRepository,
smartspaceRepository = smartspaceRepository,
- userRepository = userRepository,
appWidgetHost = mock(),
keyguardInteractor = keyguardInteractor,
editWidgetsActivityStarter = editWidgetsActivityStarter,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
+ communalSettingsInteractor = communalSettingsInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
new file mode 100644
index 0000000..b4773f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.communalSettingsInteractor by Fixture {
+ CommunalSettingsInteractor(
+ bgScope = applicationCoroutineScope,
+ repository = communalSettingsRepository,
+ userInteractor = selectedUserInteractor,
+ tableLogBuffer = mock(),
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
index 9776b43..00fdced 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
@@ -31,6 +31,7 @@
keyguardInteractor = keyguardInteractor,
communalRepository = communalRepository,
communalInteractor = communalInteractor,
+ communalSettingsInteractor = communalSettingsInteractor,
tableLogBuffer = mock(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
index 21cff0d..3b3e23e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
@@ -18,5 +18,7 @@
import com.android.systemui.kosmos.Kosmos
+var Kosmos.fakeFaceWakeUpTriggersConfig by Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() }
+
var Kosmos.faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig by
- Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() }
+ Kosmos.Fixture { fakeFaceWakeUpTriggersConfig }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index 5575b05..a8fc27a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -27,7 +27,6 @@
import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -51,7 +50,7 @@
keyguardTransitionInteractor = keyguardTransitionInteractor,
faceAuthenticationLogger = faceAuthLogger,
keyguardUpdateMonitor = keyguardUpdateMonitor,
- deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+ deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
userRepository = userRepository,
facePropertyRepository = facePropertyRepository,
faceWakeUpTriggersConfig = faceWakeUpTriggersConfig,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
index a8f45b0..6f168d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
@@ -33,6 +33,7 @@
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+ aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
keyguardClockViewModel = keyguardClockViewModel,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
index 9841778..dee3644 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
@@ -16,11 +16,17 @@
package com.android.systemui.process
+import android.os.UserHandle
+
class ProcessWrapperFake : ProcessWrapper() {
var systemUser: Boolean = false
+ var userHandle: UserHandle = UserHandle.getUserHandleForUid(0)
+
override fun isSystemUser(): Boolean {
return systemUser
}
+
+ override fun myUserHandle() = userHandle
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index e67df9d..8e430db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -26,6 +26,8 @@
class FakeQSSceneAdapter(
private val inflateDelegate: suspend (Context) -> View,
+ override val qqsHeight: Int = 0,
+ override val qsHeight: Int = 0,
) : QSSceneAdapter {
private val _customizing = MutableStateFlow(false)
override val isCustomizing: StateFlow<Boolean> = _customizing.asStateFlow()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index 998e579..37b2b76 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
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.power.domain.interactor.powerInteractor
+import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel
@@ -30,14 +34,18 @@
val Kosmos.notificationListViewModel by Fixture {
NotificationListViewModel(
- shelf = notificationShelfViewModel,
- hideListViewModel = hideListViewModel,
- footer = Optional.of(footerViewModel),
- logger = Optional.of(notificationListLoggerViewModel),
- activeNotificationsInteractor = activeNotificationsInteractor,
- keyguardTransitionInteractor = keyguardTransitionInteractor,
- seenNotificationsInteractor = seenNotificationsInteractor,
- shadeInteractor = shadeInteractor,
- zenModeInteractor = zenModeInteractor,
+ notificationShelfViewModel,
+ hideListViewModel,
+ Optional.of(footerViewModel),
+ Optional.of(notificationListLoggerViewModel),
+ activeNotificationsInteractor,
+ keyguardInteractor,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ remoteInputInteractor,
+ seenNotificationsInteractor,
+ shadeInteractor,
+ userSetupInteractor,
+ zenModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
index 8042b5c..c83710a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
@@ -23,7 +23,7 @@
import com.android.internal.logging.metricsLogger
import com.android.internal.widget.lockPatternUtils
import com.android.systemui.activityIntentHelper
-import com.android.systemui.animation.activityLaunchAnimator
+import com.android.systemui.animation.activityTransitionAnimator
import com.android.systemui.assist.assistManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
@@ -78,7 +78,7 @@
notificationPresenter,
shadeViewController,
notificationShadeWindowController,
- activityLaunchAnimator,
+ activityTransitionAnimator,
shadeAnimationInteractor,
notificationLaunchAnimatorControllerProvider,
launchFullScreenIntentProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
index 4e2dc7a..1504df4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
@@ -28,6 +28,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.process.processWrapper
import com.android.systemui.telephony.domain.interactor.telephonyInteractor
import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.utils.userRestrictionChecker
@@ -53,5 +54,6 @@
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
userRestrictionChecker = userRestrictionChecker,
+ processWrapper = processWrapper,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index 128f58b..bcb5848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.animation
+package com.android.systemui.util.settings
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+val Kosmos.fakeSettings: FakeSettings by Fixture { FakeSettings() }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a11cf8c..26c1bc9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -928,17 +928,11 @@
* Dumps all {@link AccessibilityWindowInfo}s here.
*/
void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) {
- pw.append("Global Info [ ");
- pw.println("Top focused display Id = " + mTopFocusedDisplayId);
- pw.println(" Active Window Id = " + mActiveWindowId);
- pw.println(" Top Focused Window Id = " + mTopFocusedWindowId);
- pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
- + " ]");
if (mIsProxy) {
pw.println("Proxy accessibility focused window = "
+ mProxyDisplayAccessibilityFocusedWindow);
+ pw.println();
}
- pw.println();
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int j = 0; j < windowCount; j++) {
@@ -2201,6 +2195,13 @@
* Dumps all {@link AccessibilityWindowInfo}s here.
*/
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ pw.append("Global Info [ ");
+ pw.println("Top focused display Id = " + mTopFocusedDisplayId);
+ pw.println(" Active Window Id = " + mActiveWindowId);
+ pw.println(" Top Focused Window Id = " + mTopFocusedWindowId);
+ pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
+ + " ]");
+ pw.println();
final int count = mDisplayWindowsObservers.size();
for (int i = 0; i < count; i++) {
final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index fd8ab96..e1291e5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -451,7 +451,7 @@
final Session.SaveResult saveResult = session.showSaveLocked();
- session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason);
+ session.logContextCommittedLocked(saveResult.getNoSaveUiReason(), commitReason);
if (saveResult.isLogSaveShown()) {
session.logSaveUiShown();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 83d9cdb..b89e0d8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,6 +16,8 @@
package com.android.server.autofill;
+import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR;
+import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
import static android.service.autofill.AutofillService.WEBVIEW_REQUESTED_CREDENTIAL_KEY;
@@ -113,6 +115,8 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ServiceInfo;
+import android.credentials.GetCredentialException;
+import android.credentials.GetCredentialResponse;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -123,10 +127,12 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SystemClock;
import android.service.assist.classification.FieldClassificationRequest;
import android.service.assist.classification.FieldClassificationResponse;
@@ -153,6 +159,7 @@
import android.service.autofill.SaveRequest;
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
+import android.service.credentials.CredentialProviderService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -2507,7 +2514,7 @@
+ id + " destroyed");
return;
}
- fillInIntent = createAuthFillInIntentLocked(requestId, extras, /* authExtras= */ null);
+ fillInIntent = createAuthFillInIntentLocked(requestId, extras);
if (fillInIntent == null) {
forceRemoveFromServiceLocked();
return;
@@ -2808,6 +2815,7 @@
mSessionFlags.mExpiredResponse = false;
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
+
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
if (sDebug) {
Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
@@ -2818,6 +2826,12 @@
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_SUCCESS);
replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
+ } else if (result instanceof GetCredentialResponse) {
+ Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
+ Dataset dataset = getDatasetFromCredentialResponse((GetCredentialResponse) result);
+ if (dataset != null) {
+ autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN);
+ }
} else if (result instanceof Dataset) {
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
logAuthenticationStatusLocked(requestId,
@@ -2854,6 +2868,17 @@
}
}
+ private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
+ if (result == null) {
+ return null;
+ }
+ Bundle bundle = result.getCredential().getData();
+ if (bundle == null) {
+ return null;
+ }
+ return bundle.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, Dataset.class);
+ }
+
Dataset getEffectiveDatasetForAuthentication(Dataset authenticatedDataset) {
FillResponse response = new FillResponse.Builder().addDataset(authenticatedDataset).build();
response = getEffectiveFillResponse(response);
@@ -3061,6 +3086,10 @@
* when necessary.
*/
public void logContextCommitted() {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN
+ + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE);
+ }
mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
Event.NO_SAVE_UI_REASON_NONE,
COMMIT_REASON_UNKNOWN));
@@ -3069,16 +3098,26 @@
/**
* Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
- * when necessary.
+ * when necessary. Note that it could be called before save UI is shown and the session is
+ * committed.
*
* @param saveDialogNotShowReason The reason why a save dialog was not shown.
* @param commitReason The reason why context is committed.
*/
- public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason,
+
+ @GuardedBy("mLock")
+ public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
+ + " no_save_reason:" + saveDialogNotShowReason);
+ }
mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
saveDialogNotShowReason, commitReason));
- logAllEvents(commitReason);
+
+ mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
+ mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+ mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
}
private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
@@ -3134,6 +3173,10 @@
@Nullable ArrayList<FieldClassification> detectedFieldClassifications,
@NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
+ + " no_save_reason:" + saveDialogNotShowReason);
+ }
final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
if (lastResponse == null) return;
@@ -3310,7 +3353,9 @@
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
mComponentName, mCompatMode, saveDialogNotShowReason);
- logAllEvents(commitReason);
+ mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
+ mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+ mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
}
/**
@@ -3751,11 +3796,6 @@
}
}
- if (sDebug) {
- Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
- + id + "!");
- }
-
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(new Binder(), id, client);
@@ -3787,6 +3827,10 @@
}
}
mSessionFlags.mShowingSaveUi = true;
+ if (sDebug) {
+ Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
+ + id + "!");
+ }
return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
Event.NO_SAVE_UI_REASON_NONE);
}
@@ -4690,6 +4734,11 @@
}
+ if (isCredmanIntegrationActive(response)) {
+ Slog.d(TAG, "Attempting to add Credential Manager callback to pinned entries");
+ addCredentialManagerCallback(response);
+ }
+
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
if (requestShowInlineSuggestionsLocked(response, filterText)) {
@@ -4749,6 +4798,11 @@
}
}
+ private boolean isCredmanIntegrationActive(FillResponse response) {
+ return Flags.autofillCredmanIntegration()
+ && (response.getFlags() & FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0;
+ }
+
@GuardedBy("mLock")
private void updateFillDialogTriggerIdsLocked() {
final FillResponse response = getLastResponseLocked(null);
@@ -4964,6 +5018,69 @@
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
+ private void addCredentialManagerCallback(FillResponse response) {
+ if (response.getDatasets() == null) {
+ return;
+ }
+ for (Dataset dataset: response.getDatasets()) {
+ if (isPinnedDataset(dataset)) {
+ Slog.d(TAG, "Adding Credential Manager callback to a pinned entry");
+ addCredentialManagerCallbackForDataset(dataset, response.getRequestId());
+ }
+ }
+ }
+
+ private void addCredentialManagerCallbackForDataset(Dataset dataset, int requestId) {
+ final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
+ Slog.d(TAG, "onReceiveResult from Credential Manager bottom sheet");
+ GetCredentialResponse getCredentialResponse =
+ resultData.getParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+ GetCredentialResponse.class);
+ Dataset datasetFromCredential = getDatasetFromCredentialResponse(
+ getCredentialResponse);
+ if (datasetFromCredential != null) {
+ autoFill(requestId, /*datasetIndex=*/-1,
+ datasetFromCredential, false,
+ UI_TYPE_CREDMAN_BOTTOM_SHEET);
+ }
+ } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
+ GetCredentialException exception = resultData.getParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ GetCredentialException.class);
+ Slog.d(TAG, "Credman bottom sheet from pinned "
+ + "entry failed with: + " + exception.getType() + " , "
+ + exception.getMessage());
+ } else {
+ Slog.d(TAG, "Unknown resultCode from credential "
+ + "manager bottom sheet: " + resultCode);
+ }
+ }
+ };
+ ResultReceiver ipcFriendlyResultReceiver =
+ toIpcFriendlyResultReceiver(resultReceiver);
+
+ Intent metadataIntent = dataset.getCredentialFillInIntent();
+ metadataIntent.putExtra(
+ android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ ipcFriendlyResultReceiver);
+ dataset.setCredentialFillInIntent(metadataIntent);
+ }
+
+ private ResultReceiver toIpcFriendlyResultReceiver(ResultReceiver resultReceiver) {
+ final Parcel parcel = Parcel.obtain();
+ resultReceiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ return ipcFriendly;
+ }
+
boolean isDestroyed() {
synchronized (mLock) {
return mDestroyed;
@@ -5669,8 +5786,14 @@
// does not matter the value of isPrimary because null response won't be overridden.
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH,
/* clearResponse= */ false, /* isPrimary= */ true);
- final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState,
- dataset.getAuthenticationExtras());
+ final Intent fillInIntent;
+ if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) {
+ Slog.d(TAG, "Setting credential fill intent");
+ fillInIntent = dataset.getCredentialFillInIntent();
+ } else {
+ fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
+ }
+
if (fillInIntent == null) {
forceRemoveFromServiceLocked();
return;
@@ -5686,8 +5809,7 @@
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
@GuardedBy("mLock")
@Nullable
- private Intent createAuthFillInIntentLocked(int requestId, Bundle extras,
- @Nullable Bundle authExtras) {
+ private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
final FillContext context = getFillContextByRequestIdLocked(requestId);
@@ -5704,9 +5826,6 @@
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras);
- if (authExtras != null) {
- fillInIntent.putExtra(AutofillManager.EXTRA_AUTH_STATE, authExtras);
- }
return fillInIntent;
}
@@ -6286,6 +6405,9 @@
@GuardedBy("mLock")
private void logAllEvents(@AutofillCommitReason int val) {
+ if (sVerbose) {
+ Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
+ }
mSessionCommittedEventLogger.maybeSetCommitReason(val);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
@@ -6311,6 +6433,9 @@
@GuardedBy("mLock")
RemoteFillService destroyLocked() {
// Log unlogged events.
+ if (sVerbose) {
+ Slog.v(TAG, "destroyLocked for session: " + id);
+ }
logAllEvents(COMMIT_REASON_SESSION_DESTROYED);
if (mDestroyed) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0054bc8..b43f1a9 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1305,6 +1305,8 @@
mAssociationStore.dump(out);
mDevicePresenceMonitor.dump(out);
mCompanionAppController.dump(out);
+ mTransportManager.dump(out);
+ mSystemDataTransferRequestStore.dump(out);
}
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 51c5fd6..c4c80f9 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -48,6 +48,7 @@
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -303,6 +304,32 @@
}
}
+
+
+ /**
+ * Dumps current system data transfer request states.
+ */
+ public void dump(@NonNull PrintWriter out) {
+ synchronized (mLock) {
+ out.append("System Data Transfer Requests (Cached): ");
+ if (mCachedPerUser.size() == 0) {
+ out.append("<empty>\n");
+ } else {
+ out.append("\n");
+ for (int i = 0; i < mCachedPerUser.size(); i++) {
+ final int userId = mCachedPerUser.keyAt(i);
+ for (SystemDataTransferRequest request : mCachedPerUser.get(userId)) {
+ out.append(" u")
+ .append(String.valueOf(userId))
+ .append(" -> ")
+ .append(request.toString())
+ .append('\n');
+ }
+ }
+ }
+ }
+ }
+
private void writeRequestsToXml(@NonNull TypedXmlSerializer serializer,
@Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
serializer.startTag(null, XML_TAG_REQUESTS);
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 3e45626..3861f99 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -36,6 +36,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -225,6 +226,25 @@
}
/**
+ * Dumps current list of active transports.
+ */
+ public void dump(@NonNull PrintWriter out) {
+ synchronized (mTransports) {
+ out.append("System Data Transports: ");
+ if (mTransports.size() == 0) {
+ out.append("<empty>\n");
+ } else {
+ out.append("\n");
+ for (int i = 0; i < mTransports.size(); i++) {
+ final int associationId = mTransports.keyAt(i);
+ final Transport transport = mTransports.get(associationId);
+ out.append(" ").append(transport.toString()).append('\n');
+ }
+ }
+ }
+ }
+
+ /**
* @hide
*/
public void enableSecureTransport(boolean enabled) {
diff --git a/services/companion/java/com/android/server/companion/transport/RawTransport.java b/services/companion/java/com/android/server/companion/transport/RawTransport.java
index ca169aac..05703ce 100644
--- a/services/companion/java/com/android/server/companion/transport/RawTransport.java
+++ b/services/companion/java/com/android/server/companion/transport/RawTransport.java
@@ -94,6 +94,13 @@
}
}
+ @Override
+ public String toString() {
+ return "RawTransport{"
+ + "mAssociationId=" + mAssociationId
+ + '}';
+ }
+
private void receiveMessage() throws IOException {
synchronized (mRemoteIn) {
final byte[] headerBytes = new byte[HEADER_LENGTH];
diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
index 6e906eb..1e95e65 100644
--- a/services/companion/java/com/android/server/companion/transport/SecureTransport.java
+++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
@@ -152,4 +152,12 @@
close();
}
}
+
+ @Override
+ public String toString() {
+ return "SecureTransport{"
+ + "mAssociationId=" + mAssociationId
+ + ", mSecureChannel=" + mSecureChannel
+ + '}';
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 8962bf0..1b49f18e 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -692,32 +692,37 @@
private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID;
- WaitForDevice(String deviceName, int vendorId, int productId) {
+ WaitForDevice(String deviceName, int vendorId, int productId, int associatedDisplayId) {
mListener = new InputManager.InputDeviceListener() {
@Override
public void onInputDeviceAdded(int deviceId) {
- final InputDevice device = InputManagerGlobal.getInstance().getInputDevice(
- deviceId);
- Objects.requireNonNull(device, "Newly added input device was null.");
- if (!device.getName().equals(deviceName)) {
- return;
- }
- final InputDeviceIdentifier id = device.getIdentifier();
- if (id.getVendorId() != vendorId || id.getProductId() != productId) {
- return;
- }
- mInputDeviceId = deviceId;
- mDeviceAddedLatch.countDown();
+ onInputDeviceChanged(deviceId);
}
@Override
public void onInputDeviceRemoved(int deviceId) {
-
}
@Override
public void onInputDeviceChanged(int deviceId) {
+ if (isMatchingDevice(deviceId)) {
+ mInputDeviceId = deviceId;
+ mDeviceAddedLatch.countDown();
+ }
+ }
+ private boolean isMatchingDevice(int deviceId) {
+ final InputDevice device = InputManagerGlobal.getInstance().getInputDevice(
+ deviceId);
+ Objects.requireNonNull(device, "Newly added input device was null.");
+ if (!device.getName().equals(deviceName)) {
+ return false;
+ }
+ final InputDeviceIdentifier id = device.getIdentifier();
+ if (id.getVendorId() != vendorId || id.getProductId() != productId) {
+ return false;
+ }
+ return device.getAssociatedDisplayId() == associatedDisplayId;
}
};
InputManagerGlobal.getInstance().registerInputDeviceListener(mListener, mHandler);
@@ -799,7 +804,7 @@
final int inputDeviceId;
setUniqueIdAssociation(displayId, phys);
- try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) {
+ try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId, displayId)) {
ptr = deviceOpener.get();
// See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
if (ptr == 0) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 627a62e..34c3d7e 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -246,16 +246,6 @@
@Override
public void run() {
- if (mGuid.isEmpty()) {
- Slog.e(TAG, "adbwifi guid was not set");
- return;
- }
- mPort = native_pairing_start(mGuid, mPairingCode);
- if (mPort <= 0 || mPort > 65535) {
- Slog.e(TAG, "Unable to start pairing server");
- return;
- }
-
// Register the mdns service
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName(mServiceName);
@@ -288,6 +278,28 @@
mHandler.sendMessage(message);
}
+ @Override
+ public void start() {
+ /*
+ * If a user is fast enough to click cancel, native_pairing_cancel can be invoked
+ * while native_pairing_start is running which run the destruction of the object
+ * while it is being constructed. Here we start the pairing server on foreground
+ * Thread so native_pairing_cancel can never be called concurrently. Then we let
+ * the pairing server run on a background Thread.
+ */
+ if (mGuid.isEmpty()) {
+ Slog.e(TAG, "adbwifi guid was not set");
+ return;
+ }
+ mPort = native_pairing_start(mGuid, mPairingCode);
+ if (mPort <= 0) {
+ Slog.e(TAG, "Unable to start pairing server");
+ return;
+ }
+
+ super.start();
+ }
+
public void cancelPairing() {
native_pairing_cancel();
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index adc0255..cd45b03 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -581,7 +581,8 @@
if (DEBUG_FOREGROUND_SERVICE) {
Slog.i(TAG, " Stopping fg for service " + r);
}
- setServiceForegroundInnerLocked(r, 0, null, 0, 0);
+ setServiceForegroundInnerLocked(r, 0, null, 0, 0,
+ 0);
}
}
@@ -989,7 +990,7 @@
if (fgRequired) {
logFgsBackgroundStart(r);
- if (!r.isFgsAllowedStart() && isBgFgsRestrictionEnabled(r)) {
+ if (!r.isFgsAllowedStart() && isBgFgsRestrictionEnabled(r, callingUid)) {
String msg = "startForegroundService() not allowed due to "
+ "mAllowStartForeground false: service "
+ r.shortInstanceName;
@@ -1787,11 +1788,13 @@
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, int flags, int foregroundServiceType) {
final int userId = UserHandle.getCallingUserId();
+ final int callingUid = mAm.mInjector.getCallingUid();
final long origId = mAm.mInjector.clearCallingIdentity();
try {
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
- setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);
+ setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType,
+ callingUid);
}
} finally {
mAm.mInjector.restoreCallingIdentity(origId);
@@ -2106,7 +2109,8 @@
*/
@GuardedBy("mAm")
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
- Notification notification, int flags, int foregroundServiceType) {
+ Notification notification, int flags, int foregroundServiceType,
+ int callingUidIfStart) {
if (id != 0) {
if (notification == null) {
throw new IllegalArgumentException("null notification");
@@ -2234,7 +2238,8 @@
}
// Whether FGS-BG-start restriction is enabled for this service.
- final boolean isBgFgsRestrictionEnabledForService = isBgFgsRestrictionEnabled(r);
+ final boolean isBgFgsRestrictionEnabledForService = isBgFgsRestrictionEnabled(r,
+ callingUidIfStart);
// Whether to extend the SHORT_SERVICE time out.
boolean extendShortServiceTimeout = false;
@@ -8486,14 +8491,43 @@
NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL);
}
- private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
- return mAm.mConstants.mFlagFgsStartRestrictionEnabled
- // Checking service's targetSdkVersion.
- && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid)
- && (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk
- // Checking callingUid's targetSdkVersion.
- || CompatChanges.isChangeEnabled(
- FGS_BG_START_RESTRICTION_CHANGE_ID, r.mRecentCallingUid));
+ private boolean isBgFgsRestrictionEnabled(ServiceRecord r, int actualCallingUid) {
+ // mFlagFgsStartRestrictionEnabled controls whether to enable the BG FGS restrictions:
+ // - If true (default), BG-FGS restrictions are enabled if the service targets >= S.
+ // - If false, BG-FGS restrictions are disabled for all apps.
+ if (!mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
+ return false;
+ }
+
+ // If the service target below S, then don't enable the restrictions.
+ if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid)) {
+ return false;
+ }
+
+ // mFgsStartRestrictionCheckCallerTargetSdk controls whether we take the caller's target
+ // SDK level into account or not:
+ // - If true (default), BG-FGS restrictions only happens if the caller _also_ targets >= S.
+ // - If false, BG-FGS restrictions do _not_ use the caller SDK levels.
+ if (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk) {
+ return true; // In this case, we only check the service's target SDK level.
+ }
+ final int callingUid;
+ if (Flags.newFgsRestrictionLogic()) {
+ // We always consider SYSTEM_UID to target S+, so just enable the restrictions.
+ if (actualCallingUid == Process.SYSTEM_UID) {
+ return true;
+ }
+ callingUid = actualCallingUid;
+ } else {
+ // Legacy logic used mRecentCallingUid.
+ callingUid = r.mRecentCallingUid;
+ }
+ if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, callingUid)) {
+ return false; // If the caller targets < S, then we still disable the restrictions.
+ }
+
+ // Both the service and the caller target S+, so enable the check.
+ return true;
}
private void logFgsBackgroundStart(ServiceRecord r) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index fbd32a6..d061e2d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -31,6 +31,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import java.util.function.Supplier;
@@ -202,6 +203,16 @@
}
}
+ // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
+ protected final void resetIgnoreDisplayTouches() {
+ final AidlSession session = (AidlSession) getFreshDaemon();
+ try {
+ session.getSession().setIgnoreDisplayTouches(false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when resetting setIgnoreDisplayTouches");
+ }
+ }
+
@Override
public boolean isInterruptable() {
return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
index 92218b1..199db8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -27,9 +27,8 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Allows clients (such as keyguard) to register for notifications on when biometric lockout
@@ -42,7 +41,7 @@
private final Context mContext;
@VisibleForTesting
- final List<ClientCallback> mClientCallbacks = new ArrayList<>();
+ final ConcurrentLinkedQueue<ClientCallback> mClientCallbacks = new ConcurrentLinkedQueue<>();
private static class ClientCallback {
private static final long WAKELOCK_TIMEOUT_MS = 2000;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 8121a63..93d1b6e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -232,6 +232,7 @@
handleLockout(authenticated);
if (authenticated) {
mState = STATE_STOPPED;
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -268,6 +269,7 @@
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -298,6 +300,7 @@
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -306,6 +309,7 @@
@Override
protected void startHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), getRequestReason(), this);
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
@@ -419,6 +423,7 @@
@Override
protected void stopHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -518,6 +523,7 @@
Slog.e(TAG, "Remote exception", e);
}
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -548,6 +554,7 @@
Slog.e(TAG, "Remote exception", e);
}
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index cb220b9e..8d2b46f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -87,6 +87,7 @@
@Override
protected void stopHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
unsubscribeBiometricContext();
@@ -102,6 +103,7 @@
@Override
protected void startHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 225bd59..79975e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -144,6 +144,7 @@
controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -178,6 +179,7 @@
@Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
@@ -192,6 +194,7 @@
@Override
protected void startHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
this);
if (sidefpsControllerRefactor()) {
@@ -273,6 +276,7 @@
@Override
protected void stopHalOperation() {
+ resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (sidefpsControllerRefactor()) {
mAuthenticationStateListeners.onAuthenticationStopped();
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 458fd82..05e681e 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -1020,6 +1020,10 @@
}
}
+ private boolean isAutomotive() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
Set<Integer> handles = new ArraySet<>(userProfiles.length);
@@ -1030,8 +1034,8 @@
if (Flags.cameraHsumPermission()) {
// If the device is running in headless system user mode then allow
- // User 0 to access camera.
- if (UserManager.isHeadlessSystemUserMode()) {
+ // User 0 to access camera only for automotive form factor.
+ if (UserManager.isHeadlessSystemUserMode() && isAutomotive()) {
handles.add(UserHandle.USER_SYSTEM);
}
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index c260f10..6a6e6ab 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -47,5 +47,19 @@
* @see Configuration#getGrammaticalGender
*/
public abstract @Configuration.GrammaticalGender int getSystemGrammaticalGender(int userId);
+
+ /**
+ * Retrieve the system grammatical gender.
+ *
+ * @return the value of grammatical gender
+ *
+ */
+ public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
+ Configuration configuration);
+
+ /**
+ * Whether the package can get the system grammatical gender or not.
+ */
+ public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName);
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 6eb7e95..d01f54f 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -22,17 +22,21 @@
import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.app.GrammaticalInflectionManager;
import android.app.IGrammaticalInflectionManager;
import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.Environment;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.os.Trace;
import android.permission.PermissionManager;
import android.util.AtomicFile;
import android.util.Log;
@@ -43,6 +47,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -71,6 +76,7 @@
private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
private static final String GRAMMATICAL_INFLECTION_ENABLED =
"i18n.grammatical_Inflection.enabled";
+ private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
private final GrammaticalInflectionBackupHelper mBackupHelper;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -121,16 +127,16 @@
@Override
public void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
checkCallerIsSystem();
- checkSystemTermsOfAddressIsEnabled();
GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
userId);
}
@Override
public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
- checkSystemTermsOfAddressIsEnabled();
- return GrammaticalInflectionService.this.getSystemGrammaticalGender(attributionSource,
- userId);
+ return canGetSystemGrammaticalGender(attributionSource)
+ ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+ attributionSource, userId)
+ : GRAMMATICAL_GENDER_NOT_SPECIFIED;
}
@Override
@@ -159,9 +165,33 @@
@Override
public int getSystemGrammaticalGender(int userId) {
- checkCallerIsSystem();
- return GrammaticalInflectionService.this.getSystemGrammaticalGender(
- mContext.getAttributionSource(), userId);
+ return checkSystemTermsOfAddressIsEnabled()
+ ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+ mContext.getAttributionSource(), userId)
+ : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ @Override
+ public int retrieveSystemGrammaticalGender(Configuration configuration) {
+ int systemGrammaticalGender = getSystemGrammaticalGender(mContext.getUserId());
+ // Retrieve the grammatical gender from system property, set it into
+ // configuration which will get updated later if the grammatical gender raw value of
+ // current configuration is {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
+ if (configuration.getGrammaticalGenderRaw()
+ == Configuration.GRAMMATICAL_GENDER_UNDEFINED
+ || systemGrammaticalGender <= Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+ systemGrammaticalGender = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
+ Configuration.GRAMMATICAL_GENDER_UNDEFINED);
+ }
+ return systemGrammaticalGender;
+ }
+
+ @Override
+ public boolean canGetSystemGrammaticalGender(int uid, String packageName) {
+ AttributionSource attributionSource = new AttributionSource.Builder(
+ uid).setPackageName(packageName).build();
+ return GrammaticalInflectionService.this.canGetSystemGrammaticalGender(
+ attributionSource);
}
}
@@ -202,11 +232,20 @@
}
protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+ Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender");
if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
grammaticalGender)) {
throw new IllegalArgumentException("Unknown grammatical gender");
}
+ if (!checkSystemTermsOfAddressIsEnabled()) {
+ if (grammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+ return;
+ }
+ Log.d(TAG, "Clearing the system grammatical gender setting");
+ grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
synchronized (mLock) {
final File file = getGrammaticalGenderFile(userId);
final AtomicFile atomicFile = new AtomicFile(file);
@@ -224,6 +263,15 @@
throw new RuntimeException(e);
}
}
+
+ try {
+ Configuration config = new Configuration();
+ config.setGrammaticalGender(grammaticalGender);
+ ActivityTaskManager.getService().updateConfiguration(config);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can not update configuration", e);
+ }
+ Trace.endSection();
}
public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
@@ -233,34 +281,9 @@
return GRAMMATICAL_GENDER_NOT_SPECIFIED;
}
- int callingUid = Binder.getCallingUid();
- if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) != callingUid) {
- Log.d(TAG,
- "Package " + packageName + " does not belong to the calling uid " + callingUid);
- return GRAMMATICAL_GENDER_NOT_SPECIFIED;
- }
-
- if (!checkSystemGrammaticalGenderPermission(mPermissionManager, attributionSource)) {
- return GRAMMATICAL_GENDER_NOT_SPECIFIED;
- }
-
synchronized (mLock) {
- final File file = getGrammaticalGenderFile(userId);
- if (!file.exists()) {
- Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
- return GRAMMATICAL_GENDER_NOT_SPECIFIED;
- }
-
- if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
- try {
- InputStream in = new FileInputStream(file);
- final TypedXmlPullParser parser = Xml.resolvePullParser(in);
- mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
- } catch (IOException | XmlPullParserException e) {
- Log.e(TAG, "Failed to parse XML configuration from " + file, e);
- }
- }
- return mGrammaticalGenderCache.get(userId);
+ int grammaticalGender = mGrammaticalGenderCache.get(userId);
+ return grammaticalGender < 0 ? GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender;
}
}
@@ -311,9 +334,39 @@
}
}
- private void checkSystemTermsOfAddressIsEnabled() {
+ private boolean checkSystemTermsOfAddressIsEnabled() {
if (!systemTermsOfAddressEnabled()) {
- throw new RuntimeException("The flag must be enabled to allow calling the API.");
+ Log.d(TAG, "The flag must be enabled to allow calling the API.");
+ return false;
}
+ return true;
+ }
+
+ private boolean canGetSystemGrammaticalGender(AttributionSource attributionSource) {
+ return checkSystemTermsOfAddressIsEnabled() && checkSystemGrammaticalGenderPermission(
+ mPermissionManager, attributionSource);
+ }
+
+ @Override
+ public void onUserUnlocked(TargetUser user) {
+ IoThread.getHandler().post(() -> {
+ int userId = user.getUserIdentifier();
+ final File file = getGrammaticalGenderFile(userId);
+ synchronized (mLock) {
+ if (!file.exists()) {
+ Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ return;
+ }
+ if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+ try {
+ InputStream in = new FileInputStream(file);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+ }
+ }
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e0e825d..c8c66238 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1366,6 +1366,12 @@
// we don't call onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
+ } else if (initiatedBy == INITIATED_BY_HOTPLUG
+ && mDisplayStatusCallback == null) {
+ // Force to update display status for hotplug event.
+ synchronized (mLock) {
+ announceHdmiControlStatusChange(mHdmiControlEnabled);
+ }
}
// We remove local devices here, instead of before the start of
// address allocation, to prevent multiple local devices of the
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7726609..574be34 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -621,10 +621,6 @@
mBatteryController.systemRunning();
mKeyboardBacklightController.systemRunning();
mKeyRemapper.systemRunning();
-
- mNative.setStylusPointerIconEnabled(
- Objects.requireNonNull(mContext.getSystemService(InputManager.class))
- .isStylusPointerIconEnabled());
}
private void reloadDeviceAliases() {
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 5ffc380..c02d524 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -94,7 +94,9 @@
Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SLOW_KEYS),
(reason) -> updateAccessibilitySlowKeys()),
Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
- (reason) -> updateAccessibilityStickyKeys()));
+ (reason) -> updateAccessibilityStickyKeys()),
+ Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED),
+ (reason) -> updateStylusPointerIconEnabled()));
}
/**
@@ -254,4 +256,8 @@
mNative.setMinTimeBetweenUserActivityPokes(intervalMillis);
}
}
+
+ private void updateStylusPointerIconEnabled() {
+ mNative.setStylusPointerIconEnabled(InputSettings.isStylusPointerIconEnabled(mContext));
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 91706cf..7dbe880 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -727,7 +727,7 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
- private static final IBinder ALLOWLIST_TOKEN = new Binder();
+ static final IBinder ALLOWLIST_TOKEN = new Binder();
protected RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -4759,7 +4759,7 @@
// Remove background token before returning notification to untrusted app, this
// ensures the app isn't able to perform background operations that are
// associated with notification interactions.
- notification.clearAllowlistToken();
+ notification.overrideAllowlistToken(null);
return new StatusBarNotification(
sbn.getPackageName(),
sbn.getOpPkg(),
@@ -7623,6 +7623,8 @@
}
}
+ notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
+
// Remote views? Are they too big?
checkRemoteViews(pkg, tag, id, notification);
}
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..1553618
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.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 200b17b..3468081 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -36,6 +36,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;
@@ -93,6 +94,8 @@
private final File mDiskFile;
private final Context mContext;
+ private final BackgroundInstallControlCallbackHelper mCallbackHelper;
+
private SparseSetArray<String> mBackgroundInstalledPackages = null;
// User ID -> package name -> set of foreground time frame
@@ -112,6 +115,7 @@
mHandler = new EventHandler(injector.getLooper(), this);
mDiskFile = injector.getDiskFile();
mContext = injector.getContext();
+ mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper();
UsageStatsManagerInternal usageStatsManagerInternal =
injector.getUsageStatsManagerInternal();
usageStatsManagerInternal.registerListener(
@@ -150,6 +154,16 @@
}
}
+ @Override
+ public void registerBackgroundInstallCallback(IRemoteCallback callback) {
+ mService.mCallbackHelper.registerBackgroundInstallCallback(callback);
+ }
+
+ @Override
+ public void unregisterBackgroundInstallCallback(IRemoteCallback callback) {
+ mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback);
+ }
+
}
@RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES)
@@ -274,6 +288,7 @@
initBackgroundInstalledPackages();
mBackgroundInstalledPackages.add(userId, packageName);
+ mCallbackHelper.notifyAllCallbacks(userId, packageName);
writeBackgroundInstalledPackagesToDisk();
}
@@ -568,6 +583,8 @@
File getDiskFile();
+ BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper();
+
}
private static final class InjectorImpl implements Injector {
@@ -617,5 +634,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/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 33f481c..db5acc2 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -592,9 +592,11 @@
mPm.addAllPackageProperties(pkg);
if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
- mPm.mDomainVerificationManager.addPackage(pkgSetting);
+ mPm.mDomainVerificationManager.addPackage(pkgSetting,
+ request.getPreVerifiedDomains());
} else {
- mPm.mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
+ mPm.mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting,
+ request.getPreVerifiedDomains());
}
int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5b168c4..afd4fb1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6484,6 +6484,17 @@
}
@Override
+ @Nullable
+ public ComponentName getDomainVerificationAgent() {
+ final int callerUid = Binder.getCallingUid();
+ if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) {
+ throw new SecurityException("Not allowed to query domain verification agent");
+ }
+ final Computer snapshot = snapshotComputer();
+ return getDomainVerificationAgentComponentNameLPr(snapshot);
+ }
+
+ @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e329f09..89589ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -396,6 +396,8 @@
return runArchive();
case "request-unarchive":
return runUnarchive();
+ case "get-domain-verification-agent":
+ return runGetDomainVerificationAgent();
default: {
if (ART_SERVICE_COMMANDS.contains(cmd)) {
if (DexOptHelper.useArtService()) {
@@ -4794,6 +4796,19 @@
return 0;
}
+ private int runGetDomainVerificationAgent() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ final ComponentName domainVerificationAgent = mInterface.getDomainVerificationAgent();
+ pw.println(domainVerificationAgent == null
+ ? "No Domain Verifier available!" : domainVerificationAgent.toString());
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -5194,6 +5209,9 @@
pw.println(" to unarchive an app to the responsible installer. Options are:");
pw.println(" --user: request unarchival of the app from the given user.");
pw.println("");
+ pw.println(" get-domain-verification-agent");
+ pw.println(" Displays the component name of the domain verification agent on device.");
+ pw.println("");
if (DexOptHelper.useArtService()) {
printArtServiceHelp();
} else {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index b720304..067a012 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -288,29 +288,6 @@
* configuration.
*/
private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() {
- UserProperties.Builder userPropertiesBuilder = new UserProperties.Builder()
- .setStartWithParent(true)
- .setCredentialShareableWithParent(true)
- .setAuthAlwaysRequiredToDisableQuietMode(true)
- .setAllowStoppingUserWithDelayedLocking(true)
- .setMediaSharedWithParent(false)
- .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
- .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
- .setShowInQuietMode(
- UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
- .setShowInSharingSurfaces(
- UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
- .setCrossProfileIntentFilterAccessControl(
- UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
- .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
- .setCrossProfileContentSharingStrategy(
- UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
- .setItemsRestrictedOnHomeScreen(true);
- if (android.multiuser.Flags.supportHidingProfiles()) {
- userPropertiesBuilder.setProfileApiVisibility(
- UserProperties.PROFILE_API_VISIBILITY_HIDDEN);
- }
-
return new UserTypeDetails.Builder()
.setName(USER_TYPE_PROFILE_PRIVATE)
.setBaseType(FLAG_PROFILE)
@@ -329,7 +306,26 @@
.setDarkThemeBadgeColors(
R.color.white)
.setDefaultRestrictions(getDefaultProfileRestrictions())
- .setDefaultUserProperties(userPropertiesBuilder);
+ .setDefaultUserProperties(new UserProperties.Builder()
+ .setStartWithParent(true)
+ .setCredentialShareableWithParent(true)
+ .setAuthAlwaysRequiredToDisableQuietMode(true)
+ .setAllowStoppingUserWithDelayedLocking(true)
+ .setMediaSharedWithParent(false)
+ .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
+ .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+ .setShowInQuietMode(
+ UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+ .setShowInSharingSurfaces(
+ UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
+ .setCrossProfileIntentFilterAccessControl(
+ UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
+ .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
+ .setCrossProfileContentSharingStrategy(
+ UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)
+ .setProfileApiVisibility(
+ UserProperties.PROFILE_API_VISIBILITY_HIDDEN)
+ .setItemsRestrictedOnHomeScreen(true));
}
/**
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 53ee189..7ca449a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
@@ -230,13 +231,20 @@
* broadcast will be sent to the domain verification agent so it may re-run any verification
* logic for the newly associated domains.
* <p>
- * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
- * lock. This should never be called from within the domain verification classes themselves.
+ * Optionally, the caller can specify a set of domains that are already pre-verified by the
+ * installer. These domains, if specified with autoVerify in the manifest, will be regarded as
+ * verified as soon as the app is installed, until the domain verification agent sends back the
+ * real verification results.
+ * <p>
+ * This method will mutate internal {@link DomainVerificationPkgState} and so will hold the
+ * internal lock. This should never be called from within the domain verification classes
+ * themselves.
* <p>
* This will NOT call {@link #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. That must be
* handled by the caller.
*/
- void addPackage(@NonNull PackageStateInternal newPkgSetting);
+ void addPackage(@NonNull PackageStateInternal newPkgSetting,
+ @Nullable DomainSet preVerifiedDomains);
/**
* Migrates verification state from a previous install to a new one. It is expected that the
@@ -245,14 +253,20 @@
* domains under the assumption that the new package will pass the same server side config as
* the previous package, as they have matching signatures.
* <p>
- * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
- * lock. This should never be called from within the domain verification classes themselves.
+ * Optionally, the caller can specify a set of domains that are already pre-verified by the
+ * installer. These domains, if specified with autoVerify in the manifest, will be regarded as
+ * verified as soon as the app is updated, until the domain verification agent sends back the
+ * real verification results.
+ * <p>
+ * This method will mutate internal {@link DomainVerificationPkgState} and so will hold the
+ * internal lock. This should never be called from within the domain verification classes
+ * themselves.
* <p>
* This will NOT call {@link #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. That must be
* handled by the caller.
*/
void migrateState(@NonNull PackageStateInternal oldPkgSetting,
- @NonNull PackageStateInternal newPkgSetting);
+ @NonNull PackageStateInternal newPkgSetting, @Nullable DomainSet preVerifiedDomains);
/**
* Serializes the entire internal state. This is equivalent to a full backup of the existing
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 6150099..c796b40 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.verify.domain.DomainOwner;
+import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
@@ -859,7 +860,7 @@
@Override
public void migrateState(@NonNull PackageStateInternal oldPkgSetting,
- @NonNull PackageStateInternal newPkgSetting) {
+ @NonNull PackageStateInternal newPkgSetting, @Nullable DomainSet preVerifiedDomains) {
String pkgName = newPkgSetting.getPackageName();
boolean sendBroadcast;
@@ -935,6 +936,9 @@
sendBroadcast = hasAutoVerifyDomains && needsBroadcast;
+ // Apply pre-verified states as the last step of migration
+ applyPreVerifiedState(newStateMap, newAutoVerifyDomains, preVerifiedDomains);
+
mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState(
pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates,
null /* signature */));
@@ -947,7 +951,8 @@
// TODO(b/159952358): Handle valid domainSetIds for PackageStateInternals with no AndroidPackage
@Override
- public void addPackage(@NonNull PackageStateInternal newPkgSetting) {
+ public void addPackage(@NonNull PackageStateInternal newPkgSetting,
+ @Nullable DomainSet preVerifiedDomains) {
// TODO(b/159952358): Optimize packages without any domains. Those wouldn't have to be in
// the state map, but it would require handling the "migration" case where an app either
// gains or loses all domains.
@@ -1029,6 +1034,9 @@
DomainVerificationState.STATE_MIGRATED);
}
}
+
+ // Apply pre-verified states before sending out broadcast
+ applyPreVerifiedState(pkgState.getStateMap(), autoVerifyDomains, preVerifiedDomains);
}
synchronized (mLock) {
@@ -1040,6 +1048,27 @@
}
}
+ private void applyPreVerifiedState(ArrayMap<String, Integer> stateMap,
+ ArraySet<String> autoVerifyDomains,
+ DomainSet preVerifiedDomains) {
+ // If any pre-verified domains are provided, treating them as verified as well. This
+ // allows the app to be opened immediately by the corresponding app links, but the
+ // pre-verified state can still be overwritten by the domain verification agent in the
+ // future.
+ if (preVerifiedDomains != null && !autoVerifyDomains.isEmpty()) {
+ for (String preVerifiedDomain : preVerifiedDomains.getDomains()) {
+ if (autoVerifyDomains.contains(preVerifiedDomain)
+ && !stateMap.containsKey(preVerifiedDomain)) {
+ // Only set the pre-verified state if there's no existing state
+ stateMap.put(preVerifiedDomain, DomainVerificationState.STATE_PRE_VERIFIED);
+ if (DEBUG_APPROVAL) {
+ Slog.d(TAG, "Inserted pre-verified domain: " + preVerifiedDomain);
+ }
+ }
+ }
+ }
+ }
+
/**
* Applies any immutable state as the final step when adding or migrating state. Currently only
* applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a system app.
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index 466c4c9..13b072b 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -62,6 +62,7 @@
pw.println(" - restored: preserved verification from a user data restore");
pw.println(" - legacy_failure: rejected by a legacy verifier, unknown reason");
pw.println(" - system_configured: automatically approved by the device config");
+ pw.println(" - pre_verified: the domain was pre-verified by the installer");
pw.println(" - >= 1024: Custom error code which is specific to the device verifier");
pw.println(" --user <USER_ID>: include user selections (includes all domains, not");
pw.println(" just autoVerify ones)");
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 8f8fe3c..17a9e33 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -248,8 +248,6 @@
IVibratorController vibratorController =
mVibratorControllerHolder.getVibratorController();
if (vibratorController == null) {
- Slog.d(TAG, "Unable to check if should request vibration params. "
- + "There is no registered IVibrationController.");
return false;
}
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
index b2bbcda..e1abae8 100644
--- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -32,6 +32,8 @@
import com.android.internal.infra.ServiceConnector;
+import java.io.IOException;
+
/** Manages the connection to the remote wearable sensing service. */
final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearableSensingService> {
private static final String TAG =
@@ -56,6 +58,29 @@
}
/**
+ * Provides a secure connection to the wearable.
+ *
+ * @param secureWearableConnection The secure connection to the wearable
+ * @param callback The callback for service status
+ */
+ public void provideSecureWearableConnection(
+ ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
+ if (DEBUG) {
+ Slog.i(TAG, "Providing secure wearable connection.");
+ }
+ var unused = post(
+ service -> {
+ service.provideSecureWearableConnection(secureWearableConnection, callback);
+ try {
+ // close the local fd after it has been sent to the WSS process
+ secureWearableConnection.close();
+ } catch (IOException ex) {
+ Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
+ }
+ });
+ }
+
+ /**
* Provides the implementation a data stream to the wearable.
*
* @param parcelFileDescriptor The data stream to the wearable
@@ -66,7 +91,16 @@
if (DEBUG) {
Slog.i(TAG, "Providing data stream.");
}
- post(service -> service.provideDataStream(parcelFileDescriptor, callback));
+ var unused = post(
+ service -> {
+ service.provideDataStream(parcelFileDescriptor, callback);
+ try {
+ // close the local fd after it has been sent to the WSS process
+ parcelFileDescriptor.close();
+ } catch (IOException ex) {
+ Slog.w(TAG, "Unable to close the local parcelFileDescriptor.", ex);
+ }
+ });
}
/**
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index e73fd0f..a8d6322 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -22,17 +22,19 @@
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.ambientcontext.AmbientContextEvent;
+import android.app.wearable.Flags;
import android.app.wearable.WearableSensingManager;
+import android.companion.CompanionDeviceManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
-import android.system.OsConstants;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.system.OsConstants;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -40,6 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.infra.AbstractPerUserSystemService;
+import java.io.IOException;
import java.io.PrintWriter;
/**
@@ -55,6 +58,10 @@
RemoteWearableSensingService mRemoteService;
private ComponentName mComponentName;
+ private final Object mSecureChannelLock = new Object();
+
+ @GuardedBy("mSecureChannelLock")
+ private WearableSensingSecureChannel mSecureChannel;
WearableSensingManagerPerUserService(
@NonNull WearableSensingManagerService master, Object lock, @UserIdInt int userId) {
@@ -76,6 +83,11 @@
mRemoteService = null;
}
}
+ synchronized (mSecureChannelLock) {
+ if (mSecureChannel != null) {
+ mSecureChannel.close();
+ }
+ }
}
@GuardedBy("mLock")
@@ -156,6 +168,63 @@
}
/**
+ * Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
+ * service.
+ */
+ public void onProvideWearableConnection(
+ ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
+ Slog.i(TAG, "onProvideWearableConnection in per user service.");
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Detection service is not available at this moment.");
+ notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ }
+ synchronized (mSecureChannelLock) {
+ if (mSecureChannel != null) {
+ // TODO(b/321012559): Kill the WearableSensingService process if it has not been
+ // killed from onError
+ mSecureChannel.close();
+ }
+ try {
+ mSecureChannel =
+ WearableSensingSecureChannel.create(
+ getContext().getSystemService(CompanionDeviceManager.class),
+ wearableConnection,
+ new WearableSensingSecureChannel.SecureTransportListener() {
+ @Override
+ public void onSecureTransportAvailable(
+ ParcelFileDescriptor secureTransport) {
+ Slog.i(TAG, "calling over to remote service.");
+ synchronized (mLock) {
+ ensureRemoteServiceInitiated();
+ mRemoteService.provideSecureWearableConnection(
+ secureTransport, callback);
+ }
+ }
+
+ @Override
+ public void onError() {
+ // TODO(b/321012559): Kill the WearableSensingService
+ // process if mSecureChannel has not been reassigned
+ if (Flags.enableProvideWearableConnectionApi()) {
+ notifyStatusCallback(
+ callback,
+ WearableSensingManager.STATUS_CHANNEL_ERROR);
+ }
+ }
+ });
+ } catch (IOException ex) {
+ Slog.e(TAG, "Unable to create the secure channel.", ex);
+ if (Flags.enableProvideWearableConnectionApi()) {
+ notifyStatusCallback(callback, WearableSensingManager.STATUS_CHANNEL_ERROR);
+ }
+ }
+ }
+ }
+
+ /**
* Handles sending the provided data stream for the wearable to the wearable sensing service.
*/
public void onProvideDataStream(
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 4cc2c02..28c8f87 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -211,9 +211,27 @@
private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub {
@Override
+ public void provideWearableConnection(
+ ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
+ Slog.i(TAG, "WearableSensingManagerInternal provideWearableConnection.");
+ Objects.requireNonNull(wearableConnection);
+ Objects.requireNonNull(callback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available.");
+ WearableSensingManagerPerUserService.notifyStatusCallback(
+ callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ callPerUserServiceIfExist(
+ service -> service.onProvideWearableConnection(wearableConnection, callback),
+ callback);
+ }
+
+ @Override
public void provideDataStream(
- ParcelFileDescriptor parcelFileDescriptor,
- RemoteCallback callback) {
+ ParcelFileDescriptor parcelFileDescriptor, RemoteCallback callback) {
Slog.i(TAG, "WearableSensingManagerInternal provideDataStream.");
Objects.requireNonNull(parcelFileDescriptor);
Objects.requireNonNull(callback);
diff --git a/services/core/java/com/android/server/wearable/WearableSensingSecureChannel.java b/services/core/java/com/android/server/wearable/WearableSensingSecureChannel.java
new file mode 100644
index 0000000..a16ff51
--- /dev/null
+++ b/services/core/java/com/android/server/wearable/WearableSensingSecureChannel.java
@@ -0,0 +1,350 @@
+/*
+ * 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.server.wearable;
+
+import android.annotation.NonNull;
+import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
+import android.companion.CompanionDeviceManager;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * A wrapper that manages a CompanionDeviceManager secure channel for wearable sensing.
+ *
+ * <p>This wrapper accepts a connection to a wearable from the caller. It then attaches the
+ * connection to the CompanionDeviceManager via {@link
+ * CompanionDeviceManager#attachSystemDataTransport(int, InputStream, OutputStream)}, which will
+ * create an encrypted channel using the provided connection as the raw underlying connection. The
+ * wearable device is expected to attach its side of the raw connection to its
+ * CompanionDeviceManager via the same method so that the two CompanionDeviceManagers on the two
+ * devices can perform attestation and set up the encrypted channel. Attestation requirements are
+ * listed in {@link com.android.server.security.AttestationVerificationPeerDeviceVerifier}.
+ *
+ * <p>When the encrypted channel is available, it will be provided to the caller via the
+ * SecureTransportListener.
+ */
+final class WearableSensingSecureChannel {
+
+ /** A listener for secure transport and its error signal. */
+ interface SecureTransportListener {
+
+ /** Called when the secure transport is available. */
+ void onSecureTransportAvailable(ParcelFileDescriptor secureTransport);
+
+ /**
+ * Called when there is a non-recoverable error. The secure channel will be automatically
+ * closed.
+ */
+ void onError();
+ }
+
+ private static final String TAG = WearableSensingSecureChannel.class.getSimpleName();
+ private static final String CDM_ASSOCIATION_DISPLAY_NAME = "PlaceholderDisplayNameFromWSM";
+ // The batch size of reading from the ParcelFileDescriptor returned to mSecureTransportListener
+ private static final int READ_BUFFER_SIZE = 8192;
+
+ private final Object mLock = new Object();
+ // CompanionDeviceManager (CDM) can continue to call these ExecutorServices even after the
+ // corresponding cleanup methods in CDM have been called (e.g.
+ // removeOnTransportsChangedListener). Since we shut down these ExecutorServices after
+ // clean up, we use SoftShutdownExecutor to suppress RejectedExecutionExceptions.
+ private final SoftShutdownExecutor mMessageFromWearableExecutor =
+ new SoftShutdownExecutor(Executors.newSingleThreadExecutor());
+ private final SoftShutdownExecutor mMessageToWearableExecutor =
+ new SoftShutdownExecutor(Executors.newSingleThreadExecutor());
+ private final SoftShutdownExecutor mLightWeightExecutor =
+ new SoftShutdownExecutor(Executors.newSingleThreadExecutor());
+ private final CompanionDeviceManager mCompanionDeviceManager;
+ private final ParcelFileDescriptor mUnderlyingTransport;
+ private final SecureTransportListener mSecureTransportListener;
+ private final AtomicBoolean mTransportAvailable = new AtomicBoolean(false);
+ private final Consumer<List<AssociationInfo>> mOnTransportsChangedListener =
+ this::onTransportsChanged;
+ private final BiConsumer<Integer, byte[]> mOnMessageReceivedListener = this::onMessageReceived;
+ private final ParcelFileDescriptor mRemoteFd; // To be returned to mSecureTransportListener
+ // read input received from the ParcelFileDescriptor returned to mSecureTransportListener
+ private final InputStream mLocalIn;
+ // send output to the ParcelFileDescriptor returned to mSecureTransportListener
+ private final OutputStream mLocalOut;
+
+ @GuardedBy("mLock")
+ private boolean mClosed = false;
+
+ private Integer mAssociationId = null;
+
+ /**
+ * Creates a WearableSensingSecureChannel. When the secure transport is ready,
+ * secureTransportListener will be notified.
+ *
+ * @param companionDeviceManager The CompanionDeviceManager system service.
+ * @param underlyingTransport The underlying transport to create the secure channel on.
+ * @param secureTransportListener The listener to receive the secure transport when it is ready.
+ * @throws IOException if it cannot create a {@link ParcelFileDescriptor} socket pair.
+ */
+ static WearableSensingSecureChannel create(
+ @NonNull CompanionDeviceManager companionDeviceManager,
+ @NonNull ParcelFileDescriptor underlyingTransport,
+ @NonNull SecureTransportListener secureTransportListener)
+ throws IOException {
+ Objects.requireNonNull(companionDeviceManager);
+ Objects.requireNonNull(underlyingTransport);
+ Objects.requireNonNull(secureTransportListener);
+ ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair();
+ WearableSensingSecureChannel channel =
+ new WearableSensingSecureChannel(
+ companionDeviceManager,
+ underlyingTransport,
+ secureTransportListener,
+ pair[0],
+ pair[1]);
+ channel.initialize();
+ return channel;
+ }
+
+ private WearableSensingSecureChannel(
+ CompanionDeviceManager companionDeviceManager,
+ ParcelFileDescriptor underlyingTransport,
+ SecureTransportListener secureTransportListener,
+ ParcelFileDescriptor remoteFd,
+ ParcelFileDescriptor localFd) {
+ mCompanionDeviceManager = companionDeviceManager;
+ mUnderlyingTransport = underlyingTransport;
+ mSecureTransportListener = secureTransportListener;
+ mRemoteFd = remoteFd;
+ mLocalIn = new AutoCloseInputStream(localFd);
+ mLocalOut = new AutoCloseOutputStream(localFd);
+ }
+
+ private void initialize() {
+ final long originalCallingIdentity = Binder.clearCallingIdentity();
+ try {
+ Slog.d(TAG, "Requesting CDM association.");
+ mCompanionDeviceManager.associate(
+ new AssociationRequest.Builder()
+ .setDisplayName(CDM_ASSOCIATION_DISPLAY_NAME)
+ .setSelfManaged(true)
+ .build(),
+ mLightWeightExecutor,
+ new CompanionDeviceManager.Callback() {
+ @Override
+ public void onAssociationCreated(AssociationInfo associationInfo) {
+ WearableSensingSecureChannel.this.onAssociationCreated(
+ associationInfo.getId());
+ }
+
+ @Override
+ public void onFailure(CharSequence error) {
+ Slog.e(
+ TAG,
+ "Failed to create CompanionDeviceManager association: "
+ + error);
+ onError();
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(originalCallingIdentity);
+ }
+ }
+
+ private void onAssociationCreated(int associationId) {
+ Slog.i(TAG, "CDM association created.");
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ mAssociationId = associationId;
+ mCompanionDeviceManager.addOnMessageReceivedListener(
+ mMessageFromWearableExecutor,
+ CompanionDeviceManager.MESSAGE_ONEWAY_FROM_WEARABLE,
+ mOnMessageReceivedListener);
+ mCompanionDeviceManager.addOnTransportsChangedListener(
+ mLightWeightExecutor, mOnTransportsChangedListener);
+ mCompanionDeviceManager.attachSystemDataTransport(
+ associationId,
+ new AutoCloseInputStream(mUnderlyingTransport),
+ new AutoCloseOutputStream(mUnderlyingTransport));
+ }
+ }
+
+ private void onTransportsChanged(List<AssociationInfo> associationInfos) {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ if (mAssociationId == null) {
+ Slog.e(TAG, "mAssociationId is null when transport changed");
+ return;
+ }
+ }
+ // Do not call onTransportAvailable() or onError() when holding the lock because it can
+ // cause a deadlock if the callback holds another lock.
+ boolean transportAvailable =
+ associationInfos.stream().anyMatch(info -> info.getId() == mAssociationId);
+ if (transportAvailable && mTransportAvailable.compareAndSet(false, true)) {
+ onTransportAvailable();
+ } else if (!transportAvailable && mTransportAvailable.compareAndSet(true, false)) {
+ Slog.i(TAG, "CDM transport is detached. This is not recoverable.");
+ onError();
+ }
+ }
+
+ private void onTransportAvailable() {
+ // Start sending data received from the remote stream to the wearable.
+ Slog.i(TAG, "Transport available");
+ mMessageToWearableExecutor.execute(
+ () -> {
+ int[] associationIdsToSendMessageTo = new int[] {mAssociationId};
+ byte[] buffer = new byte[READ_BUFFER_SIZE];
+ int readLen;
+ try {
+ while ((readLen = mLocalIn.read(buffer)) != -1) {
+ byte[] data = new byte[readLen];
+ System.arraycopy(buffer, 0, data, 0, readLen);
+ Slog.v(TAG, "Sending message to wearable");
+ mCompanionDeviceManager.sendMessage(
+ CompanionDeviceManager.MESSAGE_ONEWAY_TO_WEARABLE,
+ data,
+ associationIdsToSendMessageTo);
+ }
+ } catch (IOException e) {
+ Slog.i(TAG, "IOException while reading from remote stream.");
+ onError();
+ return;
+ }
+ Slog.i(
+ TAG,
+ "Reached EOF when reading from remote stream. Reporting this as an"
+ + " error.");
+ onError();
+ });
+ mSecureTransportListener.onSecureTransportAvailable(mRemoteFd);
+ }
+
+ private void onMessageReceived(int associationIdForMessage, byte[] data) {
+ if (associationIdForMessage == mAssociationId) {
+ Slog.v(TAG, "Received message from wearable.");
+ try {
+ mLocalOut.write(data);
+ mLocalOut.flush();
+ } catch (IOException e) {
+ Slog.i(
+ TAG,
+ "IOException when writing to remote stream. Closing the secure channel.");
+ onError();
+ }
+ } else {
+ Slog.v(
+ TAG,
+ "Received CDM message of type MESSAGE_ONEWAY_FROM_WEARABLE, but it is for"
+ + " another association. Ignoring the message.");
+ }
+ }
+
+ private void onError() {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ }
+ mSecureTransportListener.onError();
+ close();
+ }
+
+ /** Closes this secure channel and releases all resources. */
+ void close() {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ Slog.i(TAG, "Closing WearableSensingSecureChannel.");
+ mClosed = true;
+ if (mAssociationId != null) {
+ final long originalCallingIdentity = Binder.clearCallingIdentity();
+ try {
+ mCompanionDeviceManager.removeOnTransportsChangedListener(
+ mOnTransportsChangedListener);
+ mCompanionDeviceManager.removeOnMessageReceivedListener(
+ CompanionDeviceManager.MESSAGE_ONEWAY_FROM_WEARABLE,
+ mOnMessageReceivedListener);
+ mCompanionDeviceManager.detachSystemDataTransport(mAssociationId);
+ mCompanionDeviceManager.disassociate(mAssociationId);
+ } finally {
+ Binder.restoreCallingIdentity(originalCallingIdentity);
+ }
+ }
+ try {
+ mLocalIn.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Encountered IOException when closing local input stream.", ex);
+ }
+ try {
+ mLocalOut.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Encountered IOException when closing local output stream.", ex);
+ }
+ mMessageFromWearableExecutor.shutdown();
+ mMessageToWearableExecutor.shutdown();
+ mLightWeightExecutor.shutdown();
+ }
+ }
+
+ /**
+ * An executor that can be shutdown. Unlike an ExecutorService, it will not throw a
+ * RejectedExecutionException if {@link #execute(Runnable)} is called after shutdown.
+ */
+ private static class SoftShutdownExecutor implements Executor {
+
+ private final ExecutorService mExecutorService;
+
+ SoftShutdownExecutor(ExecutorService executorService) {
+ mExecutorService = executorService;
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ try {
+ mExecutorService.execute(runnable);
+ } catch (RejectedExecutionException ex) {
+ Slog.d(TAG, "Received new runnable after shutdown. Ignoring.");
+ }
+ }
+
+ /** Shutdown the underlying ExecutorService. */
+ void shutdown() {
+ mExecutorService.shutdown();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 19ea9f9..ee865d3 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1610,7 +1610,7 @@
Slog.i(LOG_TAG, "computeChangedWindows()");
}
- final List<WindowInfo> windows = new ArrayList<>();
+ final List<WindowInfo> windows;
final List<AccessibilityWindow> visibleWindows = new ArrayList<>();
final int topFocusedDisplayId;
IBinder topFocusedWindowToken = null;
@@ -1640,69 +1640,11 @@
}
final Display display = dc.getDisplay();
display.getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
-
- Region unaccountedSpace = mTempRegion;
- unaccountedSpace.set(0, 0, screenWidth, screenHeight);
mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
mDisplayId, visibleWindows);
- Set<IBinder> addedWindows = mTempBinderSet;
- addedWindows.clear();
- boolean focusedWindowAdded = false;
-
- final int visibleWindowCount = visibleWindows.size();
-
- // Iterate until we figure out what is touchable for the entire screen.
- for (int i = 0; i < visibleWindowCount; i++) {
- final AccessibilityWindow a11yWindow = visibleWindows.get(i);
- final Region regionInWindow = new Region();
- a11yWindow.getTouchableRegionInWindow(regionInWindow);
- if (windowMattersToAccessibility(a11yWindow, regionInWindow,
- unaccountedSpace)) {
- addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
- if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
- updateUnaccountedSpace(a11yWindow, unaccountedSpace);
- }
- focusedWindowAdded |= a11yWindow.isFocused();
- } else if (a11yWindow.isUntouchableNavigationBar()) {
- // If this widow is navigation bar without touchable region, accounting the
- // region of navigation bar inset because all touch events from this region
- // would be received by launcher, i.e. this region is a un-touchable one
- // for the application.
- unaccountedSpace.op(
- getSystemBarInsetsFrame(
- mService.mWindowMap.get(a11yWindow.getWindowInfo().token)),
- unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- }
-
- if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
- break;
- }
- }
-
- // Remove child/parent references to windows that were not added.
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- WindowInfo window = windows.get(i);
- if (!addedWindows.contains(window.parentToken)) {
- window.parentToken = null;
- }
- if (window.childTokens != null) {
- final int childTokenCount = window.childTokens.size();
- for (int j = childTokenCount - 1; j >= 0; j--) {
- if (!addedWindows.contains(window.childTokens.get(j))) {
- window.childTokens.remove(j);
- }
- }
- // Leave the child token list if empty.
- }
- }
-
- addedWindows.clear();
+ windows = buildWindowInfoListLocked(visibleWindows, mTempPoint);
// Gets the top focused display Id and window token for supporting multi-display.
topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
@@ -1718,6 +1660,74 @@
mInitialized = true;
}
+ /**
+ * From a list of windows, decides windows to be exposed to accessibility based on touchable
+ * region in the screen.
+ */
+ private List<WindowInfo> buildWindowInfoListLocked(List<AccessibilityWindow> visibleWindows,
+ Point screenSize) {
+ final List<WindowInfo> windows = new ArrayList<>();
+ final Set<IBinder> addedWindows = mTempBinderSet;
+ addedWindows.clear();
+
+ boolean focusedWindowAdded = false;
+
+ final int visibleWindowCount = visibleWindows.size();
+
+ Region unaccountedSpace = mTempRegion;
+ unaccountedSpace.set(0, 0, screenSize.x, screenSize.y);
+
+ // Iterate until we figure out what is touchable for the entire screen.
+ for (int i = 0; i < visibleWindowCount; i++) {
+ final AccessibilityWindow a11yWindow = visibleWindows.get(i);
+ final Region regionInWindow = new Region();
+ a11yWindow.getTouchableRegionInWindow(regionInWindow);
+ if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {
+ addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
+ if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
+ updateUnaccountedSpace(a11yWindow, unaccountedSpace);
+ }
+ focusedWindowAdded |= a11yWindow.isFocused();
+ } else if (a11yWindow.isUntouchableNavigationBar()) {
+ // If this widow is navigation bar without touchable region, accounting the
+ // region of navigation bar inset because all touch events from this region
+ // would be received by launcher, i.e. this region is a un-touchable one
+ // for the application.
+ unaccountedSpace.op(
+ getSystemBarInsetsFrame(
+ mService.mWindowMap.get(a11yWindow.getWindowInfo().token)),
+ unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ }
+
+ if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+ break;
+ }
+ }
+
+ // Remove child/parent references to windows that were not added.
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo window = windows.get(i);
+ if (!addedWindows.contains(window.parentToken)) {
+ window.parentToken = null;
+ }
+ if (window.childTokens != null) {
+ final int childTokenCount = window.childTokens.size();
+ for (int j = childTokenCount - 1; j >= 0; j--) {
+ if (!addedWindows.contains(window.childTokens.get(j))) {
+ window.childTokens.remove(j);
+ }
+ }
+ // Leave the child token list if empty.
+ }
+ }
+
+ addedWindows.clear();
+
+ return windows;
+ }
+
// Some windows should be excluded from unaccounted space computation, though they still
// should be reported
private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0def5a1..8773366 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -276,6 +276,7 @@
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.UserState;
import com.android.server.firewall.IntentFirewall;
+import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.sdksandbox.SdkSandboxManagerLocal;
@@ -317,7 +318,6 @@
* {@hide}
*/
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
- private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -381,6 +381,7 @@
private PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
+ GrammaticalInflectionManagerInternal mGrammaticalManagerInternal;
PendingIntentController mPendingIntentController;
IntentFirewall mIntentFirewall;
@@ -881,6 +882,8 @@
mActivityClientController.onSystemReady();
// TODO(b/258792202) Cleanup once ASM is ready to launch
ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm);
+ mGrammaticalManagerInternal = LocalServices.getService(
+ GrammaticalInflectionManagerInternal.class);
}
}
@@ -938,13 +941,8 @@
configuration.setLayoutDirection(configuration.locale);
}
- // Retrieve the grammatical gender from system property, set it into configuration which
- // will get updated later if the grammatical gender raw value of current configuration is
- // {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
- if (configuration.getGrammaticalGenderRaw() == Configuration.GRAMMATICAL_GENDER_UNDEFINED) {
- configuration.setGrammaticalGender(SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
- Configuration.GRAMMATICAL_GENDER_UNDEFINED));
- }
+ configuration.setGrammaticalGender(
+ mGrammaticalManagerInternal.retrieveSystemGrammaticalGender(configuration));
synchronized (mGlobalLock) {
mForceResizableActivities = forceResizable;
diff --git a/services/core/java/com/android/server/wm/WindowList.java b/services/core/java/com/android/server/wm/WindowList.java
index dfeba40..1e888f5 100644
--- a/services/core/java/com/android/server/wm/WindowList.java
+++ b/services/core/java/com/android/server/wm/WindowList.java
@@ -24,7 +24,7 @@
*/
class WindowList<E> extends ArrayList<E> {
- void addFirst(E e) {
+ public void addFirst(E e) {
add(0, e);
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6d2e8cc..6acf1f3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -28,6 +28,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ProcessList.INVALID_ADJ;
+import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
import static com.android.server.wm.ActivityRecord.State.DESTROYING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -298,6 +299,8 @@
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ private boolean mCanUseSystemGrammaticalGender;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
@NonNull WindowProcessListener listener) {
@@ -319,6 +322,9 @@
mIsActivityConfigOverrideAllowed = false;
}
+ mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null
+ && mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid,
+ mInfo.packageName);
onConfigurationChanged(atm.getGlobalConfiguration());
mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
}
@@ -1568,6 +1574,11 @@
return;
}
+ if (mCanUseSystemGrammaticalGender) {
+ config.setGrammaticalGender(
+ mAtm.mGrammaticalManagerInternal.getSystemGrammaticalGender(mUserId));
+ }
+
if (mPauseConfigurationDispatchCount > 0) {
mHasPendingConfigurationChange = true;
return;
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index b1349ea..f5ba50d 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -27,6 +27,7 @@
import android.credentials.selection.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.util.Slog;
@@ -149,7 +150,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation) {
+ public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
// Not needed since UI is not involved
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index b6f7eb3..be4b9e1 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -31,6 +31,7 @@
import android.credentials.selection.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
@@ -163,7 +164,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation) {
+ public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
String exception = CreateCredentialException.TYPE_USER_CANCELED;
String message = "User cancelled the selector";
if (!isUserCancellation) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 84b5cb7..534c842 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -15,6 +15,8 @@
*/
package com.android.server.credentials;
+import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;
+
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -34,7 +36,6 @@
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.service.credentials.CredentialProviderInfoFactory;
-import android.util.Slog;
import java.util.ArrayList;
import java.util.HashSet;
@@ -79,20 +80,25 @@
UserSelectionDialogResult selection = UserSelectionDialogResult
.fromResultData(resultData);
if (selection != null) {
- mCallbacks.onUiSelection(selection);
- } else {
- Slog.i(TAG, "No selection found in UI result");
+ ResultReceiver resultReceiver = resultData.getParcelable(
+ EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver.class);
+ mCallbacks.onUiSelection(selection, resultReceiver);
}
break;
case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED:
mStatus = UiStatus.TERMINATED;
- mCallbacks.onUiCancellation(/* isUserCancellation= */ true);
+ mCallbacks.onUiCancellation(/* isUserCancellation= */ true,
+ resultData.getParcelable(EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver.class));
break;
case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS:
mStatus = UiStatus.TERMINATED;
- mCallbacks.onUiCancellation(/* isUserCancellation= */ false);
+ mCallbacks.onUiCancellation(/* isUserCancellation= */ false,
+ resultData.getParcelable(EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver.class));
break;
case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE:
mStatus = UiStatus.TERMINATED;
@@ -116,10 +122,10 @@
*/
public interface CredentialManagerUiCallback {
/** Called when the user makes a selection. */
- void onUiSelection(UserSelectionDialogResult selection);
+ void onUiSelection(UserSelectionDialogResult selection, ResultReceiver resultReceiver);
/** Called when the UI is canceled without a successful provider result. */
- void onUiCancellation(boolean isUserCancellation);
+ void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver);
/** Called when the selector UI fails to come up (mostly due to parsing issue today). */
void onUiSelectorInvocationFailure();
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 9e362b3..adb1b72 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.Constants;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCandidateCredentialsException;
import android.credentials.GetCandidateCredentialsResponse;
@@ -29,10 +30,13 @@
import android.credentials.selection.GetCredentialProviderData;
import android.credentials.selection.ProviderData;
import android.credentials.selection.RequestInfo;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
+import android.service.credentials.CredentialProviderService;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
@@ -153,7 +157,8 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation) {
+ public void onUiCancellation(boolean isUserCancellation,
+ @Nullable ResultReceiver finalResponseReceiver) {
String exception = GetCandidateCredentialsException.TYPE_USER_CANCELED;
String message = "User cancelled the selector";
if (!isUserCancellation) {
@@ -161,7 +166,12 @@
message = "The UI was interrupted - please try again.";
}
mRequestSessionMetric.collectFrameworkException(exception);
- respondToClientWithErrorAndFinish(exception, message);
+ if (finalResponseReceiver != null) {
+ Bundle resultData = new Bundle();
+ finalResponseReceiver.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData);
+ } else {
+ respondToClientWithErrorAndFinish(exception, message);
+ }
}
@Override
@@ -197,7 +207,16 @@
public void onFinalResponseReceived(ComponentName componentName,
GetCredentialResponse response) {
Slog.d(TAG, "onFinalResponseReceived");
- respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response));
+ if (this.mFinalResponseReceiver != null) {
+ Slog.d(TAG, "onFinalResponseReceived sending through final receiver");
+ Bundle resultData = new Bundle();
+ resultData.putParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response);
+ mFinalResponseReceiver.send(Constants.SUCCESS_CREDMAN_SELECTOR, resultData);
+ finishSession(/*propagateCancellation=*/ false);
+ } else {
+ Slog.w(TAG, "onFinalResponseReceived result receiver not found for pinned entry");
+ }
}
/**
@@ -212,6 +231,5 @@
*/
public int getAutofillRequestId() {
return mAutofillRequestId;
-
}
}
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 4068d7b..a279337 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -31,6 +31,7 @@
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.PermissionUtils;
import android.util.Slog;
@@ -165,7 +166,7 @@
}
@Override
- public void onUiCancellation(boolean isUserCancellation) {
+ public void onUiCancellation(boolean isUserCancellation, ResultReceiver resultReceiver) {
String exception = GetCredentialException.TYPE_USER_CANCELED;
String message = "User cancelled the selector";
if (!isUserCancellation) {
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index bf7df86..633c9c4 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -17,6 +17,7 @@
package com.android.server.credentials;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -33,6 +34,7 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.UserHandle;
import android.service.credentials.CallingAppInfo;
import android.util.Slog;
@@ -101,6 +103,9 @@
protected PendingIntent mPendingIntent;
+ @Nullable
+ protected ResultReceiver mFinalResponseReceiver;
+
@NonNull
protected RequestSessionStatus mRequestSessionStatus =
RequestSessionStatus.IN_PROGRESS;
@@ -219,7 +224,8 @@
// UI callbacks
@Override // from CredentialManagerUiCallbacks
- public void onUiSelection(UserSelectionDialogResult selection) {
+ public void onUiSelection(UserSelectionDialogResult selection,
+ ResultReceiver finalResponseReceiver) {
if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
Slog.w(TAG, "Request has already been completed. This is strange.");
return;
@@ -234,6 +240,7 @@
Slog.w(TAG, "providerSession not found in onUiSelection. This is strange.");
return;
}
+ mFinalResponseReceiver = finalResponseReceiver;
ProviderSessionMetric providerSessionMetric = providerSession.mProviderSessionMetric;
int initialAuthMetricsProvider = providerSessionMetric.getBrowsedAuthenticationMetric()
.size();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 74d544f..f87fd8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -34,6 +34,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_DISPLAY;
@@ -110,6 +111,8 @@
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
+import static android.app.admin.DevicePolicyManager.ContentProtectionPolicy;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
@@ -220,6 +223,7 @@
import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
+import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled;
import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled;
import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
@@ -6012,10 +6016,10 @@
// Make sure the caller has any active admin with the right policy or
// the required permission.
if (isUnicornFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
+ admin = enforcePermissionsAndGetEnforcingAdmin(
/* admin= */ null,
- /* permission= */ MANAGE_DEVICE_POLICY_LOCK,
- USES_POLICY_FORCE_LOCK,
+ /* permissions= */ new String[]{MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE},
+ /* deviceAdminPolicy= */ USES_POLICY_FORCE_LOCK,
caller.getPackageName(),
getAffectedUser(parent)
).getActiveAdmin();
@@ -17926,6 +17930,13 @@
|| isProfileOwner(caller) || isFinancedDeviceOwner(caller));
toggleBackupServiceActive(caller.getUserId(), enabled);
+
+ if (backupServiceSecurityLogEventEnabled()) {
+ if (SecurityLog.isLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
+ caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
+ }
+ }
}
@Override
@@ -23165,6 +23176,90 @@
}
}
+ private EnforcingAdmin enforceCanCallContentProtectionLocked(
+ ComponentName who, String callerPackageName) {
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ final int userId = caller.getUserId();
+
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_CONTENT_PROTECTION,
+ caller.getPackageName(),
+ userId
+ );
+ if ((isDeviceOwner(caller) || isProfileOwner(caller))
+ && !canDPCManagedUserUseLockTaskLocked(userId)) {
+ throw new SecurityException(
+ "User " + userId + " is not allowed to use content protection");
+ }
+ return enforcingAdmin;
+ }
+
+ private void enforceCanQueryContentProtectionLocked(
+ ComponentName who, String callerPackageName) {
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ final int userId = caller.getUserId();
+
+ enforceCanQuery(MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, caller.getPackageName(), userId);
+ if ((isDeviceOwner(caller) || isProfileOwner(caller))
+ && !canDPCManagedUserUseLockTaskLocked(userId)) {
+ throw new SecurityException(
+ "User " + userId + " is not allowed to use content protection");
+ }
+ }
+
+ @Override
+ public void setContentProtectionPolicy(
+ ComponentName who, String callerPackageName, @ContentProtectionPolicy int policy)
+ throws SecurityException {
+ if (!android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled()) {
+ return;
+ }
+
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_CONTENT_PROTECTION_POLICY);
+
+ EnforcingAdmin enforcingAdmin;
+ synchronized (getLockObject()) {
+ enforcingAdmin = enforceCanCallContentProtectionLocked(who, caller.getPackageName());
+ }
+
+ if (policy == CONTENT_PROTECTION_DISABLED) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.CONTENT_PROTECTION,
+ enforcingAdmin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.CONTENT_PROTECTION,
+ enforcingAdmin,
+ new IntegerPolicyValue(policy),
+ caller.getUserId());
+ }
+ }
+
+ @Override
+ public @ContentProtectionPolicy int getContentProtectionPolicy(
+ ComponentName who, String callerPackageName) {
+ if (!android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled()) {
+ return CONTENT_PROTECTION_DISABLED;
+ }
+
+ CallerIdentity caller = getCallerIdentity(who, callerPackageName);
+ final int userHandle = caller.getUserId();
+
+ synchronized (getLockObject()) {
+ enforceCanQueryContentProtectionLocked(who, caller.getPackageName());
+ }
+ Integer policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.CONTENT_PROTECTION, userHandle);
+ if (policy == null) {
+ return CONTENT_PROTECTION_DISABLED;
+ } else {
+ return policy;
+ }
+ }
+
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
synchronized (getLockObject()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 0fc8c5e..27f1834 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -341,6 +341,13 @@
PolicyEnforcerCallbacks.setUsbDataSignalingEnabled(value, context),
new BooleanPolicySerializer());
+ static PolicyDefinition<Integer> CONTENT_PROTECTION = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY),
+ new MostRecent<>(),
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ (Integer value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ new IntegerPolicySerializer());
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -374,6 +381,8 @@
PERSONAL_APPS_SUSPENDED);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USB_DATA_SIGNALING_POLICY,
USB_DATA_SIGNALING);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY,
+ CONTENT_PROTECTION);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
new file mode 100644
index 0000000..4b578af
--- /dev/null
+++ b/services/java/com/android/server/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.server"
+
+flag {
+ namespace: "system_performance"
+ name: "telemetry_apis_service"
+ description: "Control service portion of telemetry APIs feature."
+ is_fixed_read_only: true
+ bug: "324153471"
+}
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
index d479e52..682ed91 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -32,6 +32,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..a352851 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
+++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
@@ -34,6 +34,9 @@
<option name="push-file"
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 c99e712..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
@@ -68,6 +68,20 @@
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);
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 b23f591..ac041f4 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,38 +16,59 @@
package com.android.server.pm.test.app;
-import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_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.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
@@ -74,10 +95,9 @@
PackageManager.MATCH_ALL, Process.myUserHandle()
.getIdentifier());
} catch (RemoteException e) {
- throw new RuntimeException(e);
+ throw e.rethrowFromSystemServer();
}
- },
- GET_BACKGROUND_INSTALLED_PACKAGES);
+ });
assertThat(slice).isNotNull();
var packageList = slice.getList();
@@ -94,4 +114,150 @@
.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 e.rethrowFromSystemServer();
+ }
+ });
+ 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 e.rethrowFromSystemServer();
+ }
+ });
+ 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();
+ }
}
\ No newline at end of file
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/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d307608..b374af6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -149,8 +149,8 @@
callingUidInt.set(it.callingUid)
callingUserIdInt.set(it.callingUserId)
service.proxy = it.proxy
- service.addPackage(visiblePkgState)
- service.addPackage(invisiblePkgState)
+ service.addPackage(visiblePkgState, null)
+ service.addPackage(invisiblePkgState, null)
service.block(it)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 5edf30a3..a8100af 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -566,7 +566,7 @@
}
private fun DomainVerificationService.addPackages(vararg pkgStates: PackageStateInternal) =
- pkgStates.forEach(::addPackage)
+ pkgStates.forEach {pkg: PackageStateInternal -> addPackage(pkg, null)}
private fun makeManager(service: DomainVerificationService, userId: Int) =
DomainVerificationManager(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 85f0125..e0407c1 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.Signature
import android.content.pm.SigningDetails
import android.content.pm.verify.domain.DomainOwner
+import android.content.pm.verify.domain.DomainSet
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
@@ -48,16 +49,16 @@
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
-import java.io.ByteArrayInputStream
-import java.io.ByteArrayOutputStream
-import java.security.PublicKey
-import java.util.UUID
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.doReturn
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.security.PublicKey
+import java.util.UUID
class DomainVerificationPackageTest {
@@ -88,7 +89,7 @@
@Test
fun addPackageFirstTime() {
val service = makeService(pkg1, pkg2)
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
val info = service.getInfo(pkg1.packageName)
assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
@@ -120,8 +121,8 @@
systemConfiguredPackageNames = ArraySet(setOf(pkg1.packageName, pkg2.packageName)),
pkg1, pkg2
)
- service.addPackage(pkg1)
- service.addPackage(pkg2)
+ service.addPackage(pkg1, null)
+ service.addPackage(pkg2, null)
service.getInfo(pkg1.packageName).apply {
assertThat(packageName).isEqualTo(pkg1.packageName)
@@ -198,7 +199,7 @@
val service = makeService(pkg1, pkg2)
val computer = mockComputer(pkg1, pkg2)
service.restoreSettings(computer, Xml.resolvePullParser(xml.byteInputStream()))
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
val info = service.getInfo(pkg1.packageName)
assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
@@ -248,7 +249,7 @@
val service = makeService(pkg1, pkg2)
val computer = mockComputer(pkg1, pkg2)
service.restoreSettings(computer, Xml.resolvePullParser(xml.byteInputStream()))
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
val info = service.getInfo(pkg1.packageName)
assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
@@ -307,7 +308,7 @@
service.readSettings(computer, Xml.resolvePullParser(it))
}
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
assertAddPackageActivePendingRestoredState(service)
}
@@ -321,7 +322,7 @@
service.readSettings(computer, Xml.resolvePullParser(it))
}
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
val userState = service.getUserState(pkg1.packageName)
assertThat(userState.packageName).isEqualTo(pkg1.packageName)
@@ -345,11 +346,55 @@
service.restoreSettings(computer, Xml.resolvePullParser(it))
}
- service.addPackage(pkg1)
+ service.addPackage(pkg1, null)
assertAddPackageActivePendingRestoredState(service, expectRestore = true)
}
+ @Test
+ fun addPackageWithPreVerifiedDomains() {
+ val service = makeService(pkg1)
+ val pkg1 = mockPkgState(
+ PKG_ONE,
+ UUID_ONE,
+ SIGNATURE_ONE,
+ autoVerifyDomains = listOf(DOMAIN_1, DOMAIN_2),
+ otherDomains = listOf(DOMAIN_3, DOMAIN_4))
+ service.addPackage(pkg1, DomainSet(setOf(DOMAIN_1, DOMAIN_3)))
+ val info = service.getInfo(pkg1.packageName)
+ assertThat(info.packageName).isEqualTo(pkg1.packageName)
+ assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
+ // Test that DOMAIN_1 is pre-verified and DOMAIN_3 is ignored because autoVerify=false
+ assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_MODIFIABLE_VERIFIED,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
+ assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
+ assertThat(userState.user.identifier).isEqualTo(USER_ID)
+ assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.packageName)
+
+ // Test that the pre-verified state can be overwritten to be disapproved
+ service.setDomainVerificationStatusInternal(
+ PKG_ONE,
+ DomainVerificationState.STATE_DENIED,
+ ArraySet(setOf(DOMAIN_1, DOMAIN_2)))
+ val infoUpdated = service.getInfo(pkg1.packageName)
+ assertThat(infoUpdated.hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_UNMODIFIABLE,
+ ))
+ }
+
/**
* Shared string that contains invalid [DOMAIN_3] and [DOMAIN_4] which should be stripped from
* the final state.
@@ -447,7 +492,7 @@
val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
- service.addPackage(pkgBefore)
+ service.addPackage(pkgBefore, null)
// Only insert the package after addPackage call to ensure the service doesn't access
// a live package inside the addPackage logic. It should only use the provided input.
@@ -482,7 +527,7 @@
map[pkgName] = pkgAfter
- service.migrateState(pkgBefore, pkgAfter)
+ service.migrateState(pkgBefore, pkgAfter, null)
assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
DOMAIN_1 to STATE_UNMODIFIABLE,
@@ -503,7 +548,7 @@
val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
- service.addPackage(pkgBefore)
+ service.addPackage(pkgBefore, null)
// Only insert the package after addPackage call to ensure the service doesn't access
// a live package inside the addPackage logic. It should only use the provided input.
@@ -522,7 +567,7 @@
// Now remove the package because migrateState shouldn't use it either
map.remove(pkgName)
- service.migrateState(pkgBefore, pkgAfter)
+ service.migrateState(pkgBefore, pkgAfter, null)
map[pkgName] = pkgAfter
@@ -550,7 +595,7 @@
val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
- service.addPackage(pkgBefore)
+ service.addPackage(pkgBefore, null)
// Only insert the package after addPackage call to ensure the service doesn't access
// a live package inside the addPackage logic. It should only use the provided input.
@@ -571,7 +616,7 @@
// Now remove the package because migrateState shouldn't use it either
map.remove(pkgName)
- service.migrateState(pkgBefore, pkgAfter)
+ service.migrateState(pkgBefore, pkgAfter, null)
map[pkgName] = pkgAfter
@@ -596,7 +641,7 @@
val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
- service.addPackage(pkgBefore)
+ service.addPackage(pkgBefore, null)
// Only insert the package after addPackage call to ensure the service doesn't access
// a live package inside the addPackage logic. It should only use the provided input.
@@ -615,7 +660,7 @@
// Now remove the package because migrateState shouldn't use it either
map.remove(pkgName)
- service.migrateState(pkgBefore, pkgAfter)
+ service.migrateState(pkgBefore, pkgAfter, null)
map[pkgName] = pkgAfter
@@ -640,7 +685,7 @@
val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
- service.addPackage(pkgBefore)
+ service.addPackage(pkgBefore, null)
// Only insert the package after addPackage call to ensure the service doesn't access
// a live package inside the addPackage logic. It should only use the provided input.
@@ -667,7 +712,7 @@
// Now remove the package because migrateState shouldn't use it either
map.remove(pkgName)
- service.migrateState(pkgBefore, pkgAfter)
+ service.migrateState(pkgBefore, pkgAfter, null)
map[pkgName] = pkgAfter
@@ -685,6 +730,30 @@
}
@Test
+ fun migratePackageWithPreVerifiedDomains() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
+
+ val map = mutableMapOf<String, PackageStateInternal>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore, null)
+ service.migrateState(pkgBefore, pkgAfter, DomainSet(setOf(DOMAIN_1, DOMAIN_3)))
+
+ map[pkgName] = pkgAfter
+
+ assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_MODIFIABLE_VERIFIED,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ ))
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
fun backupAndRestore() {
// This test acts as a proxy for true user restore through PackageManager,
// as that's much harder to test for real.
@@ -694,8 +763,8 @@
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
val serviceBefore = makeService(pkg1, pkg2)
val computerBefore = mockComputer(pkg1, pkg2)
- serviceBefore.addPackage(pkg1)
- serviceBefore.addPackage(pkg2)
+ serviceBefore.addPackage(pkg1, null)
+ serviceBefore.addPackage(pkg2, null)
serviceBefore.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), STATE_SUCCESS)
serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.packageName, false, 10)
@@ -748,8 +817,8 @@
val serviceAfter = makeService(pkg1, pkg2)
val computerAfter = mockComputer(pkg1, pkg2)
- serviceAfter.addPackage(pkg1)
- serviceAfter.addPackage(pkg2)
+ serviceAfter.addPackage(pkg1, null)
+ serviceAfter.addPackage(pkg2, null)
// Check the state is default before the restoration applies
listOf(0, 10).forEach {
@@ -858,8 +927,8 @@
)
val service = makeService(pkg1, pkg2)
- service.addPackage(pkg1)
- service.addPackage(pkg2)
+ service.addPackage(pkg1, null)
+ service.addPackage(pkg2, null)
// Approve domain 1, 3, and 4 for package 2 for both users
USER_IDS.forEach {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index a5c4f6c..9748307 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -106,7 +106,7 @@
fun service(name: String, block: DomainVerificationService.() -> Unit) =
Params(makeService, name) { service ->
service.proxy = proxy
- service.addPackage(mockPkgState())
+ service.addPackage(mockPkgState(), null)
service.block()
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index ae570a3..56ab841 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -97,8 +97,8 @@
}
}
})
- addPackage(pkg1)
- addPackage(pkg2)
+ addPackage(pkg1, null)
+ addPackage(pkg2, null)
// Starting state for all tests is to have domain 1 enabled for the first package
setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true, USER_ID)
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..574f369
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.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 BackgroundInstallControlCallbackHelper} */
+@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/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 9cdaec6..7a77392 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -472,6 +472,7 @@
verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
verify(mSideFpsController).hide(anyInt());
+ verify(mHal, times(2)).setIgnoreDisplayTouches(false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 951c9393..3ee54f5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -327,6 +328,7 @@
verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
verify(mSideFpsController).hide(anyInt());
+ verify(mHal, times(2)).setIgnoreDisplayTouches(false);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 5943832..07e6ab2 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -19,11 +19,10 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith;
-import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManagerInternal;
@@ -86,9 +85,8 @@
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.uniqueId = "uniqueId";
- doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ setUpDisplay(1 /* displayId */);
+ setUpDisplay(2 /* displayId */);
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
@@ -100,6 +98,16 @@
threadVerifier);
}
+ void setUpDisplay(int displayId) {
+ final String uniqueId = "uniqueId:" + displayId;
+ doAnswer((inv) -> {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = uniqueId;
+ return displayInfo;
+ }).when(mDisplayManagerInternalMock).getDisplayInfo(eq(displayId));
+ mInputManagerMockHelper.addDisplayIdMapping(uniqueId, displayId);
+ }
+
@After
public void tearDown() {
mInputManagerMockHelper.tearDown();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index 3722247..74e854e4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -20,7 +20,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import android.hardware.input.IInputDevicesChangedListener;
@@ -28,12 +27,15 @@
import android.hardware.input.InputManagerGlobal;
import android.os.RemoteException;
import android.testing.TestableLooper;
+import android.view.Display;
import android.view.InputDevice;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.stream.IntStream;
@@ -49,6 +51,10 @@
private final InputManagerGlobal.TestSession mInputManagerGlobalSession;
private final List<InputDevice> mDevices = new ArrayList<>();
private IInputDevicesChangedListener mDevicesChangedListener;
+ private final Map<String /* uniqueId */, Integer /* displayId */> mDisplayIdMapping =
+ new HashMap<>();
+ private final Map<String /* phys */, String /* uniqueId */> mUniqueIdAssociation =
+ new HashMap<>();
InputManagerMockHelper(TestableLooper testableLooper,
InputController.NativeWrapper nativeWrapperMock, IInputManager iInputManagerMock)
@@ -73,8 +79,10 @@
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
doAnswer(inv -> mDevices.get(inv.getArgument(0)))
.when(mIInputManagerMock).getInputDevice(anyInt());
- doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
- doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
+ doAnswer(inv -> mUniqueIdAssociation.put(inv.getArgument(0), inv.getArgument(1))).when(
+ mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
+ doAnswer(inv -> mUniqueIdAssociation.remove(inv.getArgument(0))).when(
+ mIInputManagerMock).removeUniqueIdAssociation(anyString());
// Set a new instance of InputManager for testing that uses the IInputManager mock as the
// interface to the server.
@@ -87,17 +95,25 @@
}
}
+ public void addDisplayIdMapping(String uniqueId, int displayId) {
+ mDisplayIdMapping.put(uniqueId, displayId);
+ }
+
private long handleNativeOpenInputDevice(InvocationOnMock inv) {
Objects.requireNonNull(mDevicesChangedListener,
"InputController did not register an InputDevicesChangedListener.");
+ final String phys = inv.getArgument(3);
final InputDevice device = new InputDevice.Builder()
.setId(mDevices.size())
.setName(inv.getArgument(0))
.setVendorId(inv.getArgument(1))
.setProductId(inv.getArgument(2))
- .setDescriptor(inv.getArgument(3))
+ .setDescriptor(phys)
.setExternal(true)
+ .setAssociatedDisplayId(
+ mDisplayIdMapping.getOrDefault(mUniqueIdAssociation.get(phys),
+ Display.INVALID_DISPLAY))
.build();
mDevices.add(device);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 5442af8..157e893 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -39,6 +39,7 @@
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -2015,6 +2016,13 @@
eq(virtualDevice), any(), any())).thenReturn(displayId);
virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback,
NONBLOCKED_APP_PACKAGE_NAME);
+ final String uniqueId = UNIQUE_ID + displayId;
+ doAnswer(inv -> {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = uniqueId;
+ return displayInfo;
+ }).when(mDisplayManagerInternalMock).getDisplayInfo(eq(displayId));
+ mInputManagerMockHelper.addDisplayIdMapping(uniqueId, displayId);
}
private ComponentName getPermissionDialogComponent() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 99fa30c..1d3dacc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -169,14 +169,14 @@
.setEarcSupported(true)
.build();
mHdmiPortInfo[3] =
- new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+ new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000)
.setCecSupported(true)
.setMhlSupported(false)
.setArcSupported(false)
.setEarcSupported(false)
.build();
mHdmiPortInfo[4] =
- new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000)
+ new HdmiPortInfo.Builder(5, HdmiPortInfo.PORT_OUTPUT, 0x3000)
.setCecSupported(true)
.setMhlSupported(false)
.setArcSupported(false)
@@ -841,6 +841,65 @@
}
@Test
+ public void onHotPlugIn_CecDisabledOnTv_CecNotAvailable() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.onHotplug(4, true);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ // Wait for DevicePowerStatusAction to finish.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse();
+ }
+
+ @Test
+ public void onHotPlugIn_CecEnabledOnTv_CecAvailable() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.onHotplug(4, true);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_TV);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_ON);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+ mNativeWrapper.onCecMessage(reportPowerStatus);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue();
+ }
+ @Test
public void handleCecCommand_errorParameter_returnsAbortInvalidOperand() {
// Validity ERROR_PARAMETER. Taken from HdmiCecMessageValidatorTest#isValid_menuStatus
HdmiCecMessage message = HdmiUtils.buildMessage("80:8D:03");
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 8656f60..bf87e3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -111,6 +111,8 @@
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private PermissionManagerServiceInternal mPermissionManager;
+ @Mock
+ private BackgroundInstallControlCallbackHelper mCallbackHelper;
@Captor
private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;
@@ -982,5 +984,11 @@
public File getDiskFile() {
return mFile;
}
+
+
+ @Override
+ public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() {
+ return mCallbackHelper;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 3778a32..f1d3ba9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -23,7 +23,6 @@
import android.content.pm.UserProperties;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Xml;
import androidx.test.filters.MediumTest;
@@ -32,7 +31,6 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,13 +52,10 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserManagerServiceUserPropertiesTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
/** Test that UserProperties can properly read the xml information that it writes. */
@Test
public void testWriteReadXml() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
final UserProperties defaultProps = new UserProperties.Builder()
.setShowInLauncher(21)
.setStartWithParent(false)
@@ -123,7 +118,6 @@
/** Tests parcelling an object in which all properties are present. */
@Test
public void testParcelUnparcel() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
final UserProperties originalProps = new UserProperties.Builder()
.setShowInLauncher(2145)
.build();
@@ -134,7 +128,6 @@
/** Tests copying a UserProperties object varying permissions. */
@Test
public void testCopyLacksPermissions() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
final UserProperties defaultProps = new UserProperties.Builder()
.setShowInLauncher(2145)
.setStartWithParent(true)
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 6cdbc74..3047bcf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -41,7 +41,6 @@
import android.os.Bundle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.InstrumentationRegistry;
@@ -51,7 +50,6 @@
import com.android.frameworks.servicestests.R;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,11 +71,8 @@
public void setup() {
mResources = InstrumentationRegistry.getTargetContext().getResources();
}
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
public void testUserTypeBuilder_createUserType() {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
final Bundle systemSettings = makeSettingsBundle("s1", "s2");
final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
@@ -207,7 +202,6 @@
@Test
public void testUserTypeBuilder_defaults() {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
UserTypeDetails type = new UserTypeDetails.Builder()
.setName("name") // Required (no default allowed)
.setBaseType(FLAG_FULL) // Required (no default allowed)
@@ -321,7 +315,6 @@
/** Tests {@link UserTypeFactory#customizeBuilders} for a reasonable xml file. */
@Test
public void testUserTypeFactoryCustomize_profile() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
final String userTypeAosp1 = "android.test.1"; // Profile user that is not customized
final String userTypeAosp2 = "android.test.2"; // Profile user that is customized
final String userTypeOem1 = "custom.test.1"; // Custom-defined profile
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 9323b48..df2069e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -39,7 +39,6 @@
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -56,7 +55,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -99,8 +97,6 @@
private UserSwitchWaiter mUserSwitchWaiter;
private UserRemovalWaiter mUserRemovalWaiter;
private int mOriginalCurrentUserId;
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() throws Exception {
@@ -172,7 +168,6 @@
@Test
public void testCloneUser() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
assumeCloneEnabled();
UserHandle mainUser = mUserManager.getMainUser();
assumeTrue("Main user is null", mainUser != null);
@@ -229,7 +224,8 @@
.isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy());
assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
- assertThrows(SecurityException.class, cloneUserProperties::getProfileApiVisibility);
+ assertThat(typeProps.getProfileApiVisibility()).isEqualTo(
+ cloneUserProperties.getProfileApiVisibility());
compareDrawables(mUserManager.getUserBadge(),
Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
@@ -311,7 +307,6 @@
@Test
public void testPrivateProfile() throws Exception {
- mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
UserHandle mainUser = mUserManager.getMainUser();
assumeTrue("Main user is null", mainUser != null);
// Get the default properties for private profile user type.
@@ -353,8 +348,8 @@
assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
assertThrows(SecurityException.class,
privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking);
- assertThrows(SecurityException.class,
- privateProfileUserProperties::getProfileApiVisibility);
+ assertThat(typeProps.getProfileApiVisibility()).isEqualTo(
+ privateProfileUserProperties.getProfileApiVisibility());
assertThrows(SecurityException.class,
privateProfileUserProperties::areItemsRestrictedOnHomeScreen);
compareDrawables(mUserManager.getUserBadge(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f6cf4da..77be01c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -323,6 +323,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -12971,6 +12972,35 @@
}
@Test
+ public void fixNotification_customAllowlistToken()
+ throws Exception {
+ Notification n = new Notification.Builder(mContext, "test")
+ .build();
+ try {
+ Field allowlistToken = Class.forName("android.app.Notification").
+ getDeclaredField("mAllowlistToken");
+ allowlistToken.setAccessible(true);
+ allowlistToken.set(n, new Binder());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
+
+ IBinder actual = null;
+ try {
+ Field allowlistToken = Class.forName("android.app.Notification").
+ getDeclaredField("mAllowlistToken");
+ allowlistToken.setAccessible(true);
+ actual = (IBinder) allowlistToken.get(n);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ assertTrue(mService.ALLOWLIST_TOKEN == actual);
+ }
+
+ @Test
public void testCancelAllNotifications_IgnoreUserInitiatedJob() throws Exception {
when(mJsi.isNotificationAssociatedWithAnyUserInitiatedJobs(anyInt(), anyInt(), anyString()))
.thenReturn(true);
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 331caa1..7ad26c9 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,10 @@
package android.telecom;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,6 +28,8 @@
import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
+import com.android.server.telecom.flags.Flags;
+
import java.util.Objects;
/**
@@ -169,7 +174,9 @@
}
/**
- * Creates a new DisconnectCause instance.
+ * Creates a new DisconnectCause instance. This is used by Telephony to pass in extra debug
+ * info to Telecom regarding the disconnect cause.
+ *
* @param code The code for the disconnect cause.
* @param label The localized label to show to the user to explain the disconnect.
* @param description The localized description to show to the user to explain the disconnect.
@@ -180,7 +187,10 @@
* @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
* @hide
*/
- public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ public DisconnectCause(int code, @NonNull CharSequence label,
+ @NonNull CharSequence description, @NonNull String reason,
int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
@Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
@Nullable ImsReasonInfo imsReasonInfo) {
@@ -241,28 +251,40 @@
}
/**
- * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+ * Returns the telephony {@link android.telephony.DisconnectCause} for the call. This is only
+ * used internally by Telecom for providing extra debug information from Telephony.
+ *
* @return The disconnect cause.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
return mTelephonyDisconnectCause;
}
/**
- * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+ * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call. This is
+ * only used internally by Telecom for providing extra debug information from Telephony.
+ *
* @return The precise disconnect cause.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
return mTelephonyPreciseDisconnectCause;
}
/**
- * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This is
+ * only used internally by Telecom for providing extra debug information from Telephony.
+ *
* @return The {@link ImsReasonInfo} or {@code null} if not known.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public @Nullable ImsReasonInfo getImsReasonInfo() {
return mImsReasonInfo;
}
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index b59e855..5af2c34 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,6 +39,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.telephony.flags.Flags;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Retention;
@@ -759,6 +762,20 @@
public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
/**
+ * Return the available memory in bytes of the eUICC.
+ *
+ * @param slotId ID of the SIM slot being queried.
+ * @return the available memory in bytes.
+ * @see android.telephony.euicc.EuiccManager#getAvailableMemoryInBytes
+ */
+ @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+ public long onGetAvailableMemoryInBytes(int slotId) {
+ // stub implementation, LPA needs to implement this
+ throw new UnsupportedOperationException("The connected LPA does not implement"
+ + "EuiccService#onGetAvailableMemoryInBytes(int)");
+ }
+
+ /**
* Dump to a provided printWriter.
*/
public void dump(@NonNull PrintWriter printWriter) {
@@ -834,6 +851,22 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+ public void getAvailableMemoryInBytes(
+ int slotId, IGetAvailableMemoryInBytesCallback callback) {
+ mExecutor.execute(
+ () -> {
+ long availableMemoryInBytes =
+ EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+ try {
+ callback.onSuccess(availableMemoryInBytes);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ });
+ }
+
+ @Override
public void startOtaIfNecessary(
int slotId, IOtaStatusChangedCallback statusChangedCallback) {
mExecutor.execute(new Runnable() {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index f8d5ae9..0f8c72b 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -19,6 +19,7 @@
import android.service.euicc.IDeleteSubscriptionCallback;
import android.service.euicc.IDownloadSubscriptionCallback;
import android.service.euicc.IEraseSubscriptionsCallback;
+import android.service.euicc.IGetAvailableMemoryInBytesCallback;
import android.service.euicc.IGetDefaultDownloadableSubscriptionListCallback;
import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
import android.service.euicc.IGetEidCallback;
@@ -60,4 +61,5 @@
void retainSubscriptionsForFactoryReset(
int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
void dump(in IEuiccServiceDumpResultCallback callback);
-}
\ No newline at end of file
+ void getAvailableMemoryInBytes(int slotId, in IGetAvailableMemoryInBytesCallback callback);
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
index 128f58b..bd6d19b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.animation
+package android.service.euicc;
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+/** @hide */
+oneway interface IGetAvailableMemoryInBytesCallback {
+ void onSuccess(long availableMemoryInBytes);
+}
diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java
index 0f54e8d..3c11da5 100644
--- a/telephony/java/android/telephony/DomainSelectionService.java
+++ b/telephony/java/android/telephony/DomainSelectionService.java
@@ -90,7 +90,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
-public class DomainSelectionService extends Service {
+public abstract class DomainSelectionService extends Service {
private static final String LOG_TAG = "DomainSelectionService";
@@ -152,7 +152,7 @@
private boolean mIsExitedFromAirplaneMode;
private @Nullable ImsReasonInfo mImsReasonInfo;
private @PreciseDisconnectCauses int mCause;
- private @Nullable EmergencyRegResult mEmergencyRegResult;
+ private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
/**
* @param slotIndex The logical slot index.
@@ -172,7 +172,7 @@
@Nullable Uri address, @SelectorType int selectorType,
boolean video, boolean emergency, boolean isTest, boolean exited,
@Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause,
- @Nullable EmergencyRegResult regResult) {
+ @Nullable EmergencyRegistrationResult regResult) {
mSlotIndex = slotIndex;
mSubId = subscriptionId;
mCallId = callId;
@@ -184,7 +184,7 @@
mIsExitedFromAirplaneMode = exited;
mImsReasonInfo = imsReasonInfo;
mCause = cause;
- mEmergencyRegResult = regResult;
+ mEmergencyRegistrationResult = regResult;
}
/**
@@ -204,7 +204,7 @@
mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode;
mImsReasonInfo = s.mImsReasonInfo;
mCause = s.mCause;
- mEmergencyRegResult = s.mEmergencyRegResult;
+ mEmergencyRegistrationResult = s.mEmergencyRegistrationResult;
}
/**
@@ -296,8 +296,8 @@
/**
* @return The current registration state of cellular network.
*/
- public @Nullable EmergencyRegResult getEmergencyRegResult() {
- return mEmergencyRegResult;
+ public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() {
+ return mEmergencyRegistrationResult;
}
@Override
@@ -313,7 +313,7 @@
+ ", airplaneMode=" + mIsExitedFromAirplaneMode
+ ", reasonInfo=" + mImsReasonInfo
+ ", cause=" + mCause
- + ", regResult=" + mEmergencyRegResult
+ + ", regResult=" + mEmergencyRegistrationResult
+ " }";
}
@@ -331,14 +331,15 @@
&& mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode
&& equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo)
&& mCause == that.mCause
- && equalsHandlesNulls(mEmergencyRegResult, that.mEmergencyRegResult);
+ && equalsHandlesNulls(mEmergencyRegistrationResult,
+ that.mEmergencyRegistrationResult);
}
@Override
public int hashCode() {
return Objects.hash(mCallId, mAddress, mImsReasonInfo,
mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode,
- mEmergencyRegResult, mSlotIndex, mSubId, mSelectorType, mCause);
+ mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause);
}
@Override
@@ -359,7 +360,7 @@
out.writeBoolean(mIsExitedFromAirplaneMode);
out.writeParcelable(mImsReasonInfo, 0);
out.writeInt(mCause);
- out.writeParcelable(mEmergencyRegResult, 0);
+ out.writeParcelable(mEmergencyRegistrationResult, 0);
}
private void readFromParcel(@NonNull Parcel in) {
@@ -376,8 +377,9 @@
mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(),
android.telephony.ims.ImsReasonInfo.class);
mCause = in.readInt();
- mEmergencyRegResult = in.readParcelable(EmergencyRegResult.class.getClassLoader(),
- EmergencyRegResult.class);
+ mEmergencyRegistrationResult = in.readParcelable(
+ EmergencyRegistrationResult.class.getClassLoader(),
+ EmergencyRegistrationResult.class);
}
public static final @NonNull Creator<SelectionAttributes> CREATOR =
@@ -413,7 +415,7 @@
private boolean mIsExitedFromAirplaneMode;
private @Nullable ImsReasonInfo mImsReasonInfo;
private @PreciseDisconnectCauses int mCause;
- private @Nullable EmergencyRegResult mEmergencyRegResult;
+ private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
/**
* Default constructor for Builder.
@@ -430,7 +432,7 @@
* @param callId The call identifier.
* @return The same instance of the builder.
*/
- public @NonNull Builder setCallId(@NonNull String callId) {
+ public @NonNull Builder setCallId(@Nullable String callId) {
mCallId = callId;
return this;
}
@@ -441,7 +443,7 @@
* @param address The dialed address.
* @return The same instance of the builder.
*/
- public @NonNull Builder setAddress(@NonNull Uri address) {
+ public @NonNull Builder setAddress(@Nullable Uri address) {
mAddress = address;
return this;
}
@@ -497,7 +499,7 @@
* @param info The reason why the last PS attempt failed.
* @return The same instance of the builder.
*/
- public @NonNull Builder setPsDisconnectCause(@NonNull ImsReasonInfo info) {
+ public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) {
mImsReasonInfo = info;
return this;
}
@@ -519,8 +521,9 @@
* @param regResult The current registration result for emergency services.
* @return The same instance of the builder.
*/
- public @NonNull Builder setEmergencyRegResult(@NonNull EmergencyRegResult regResult) {
- mEmergencyRegResult = regResult;
+ public @NonNull Builder setEmergencyRegistrationResult(
+ @Nullable EmergencyRegistrationResult regResult) {
+ mEmergencyRegistrationResult = regResult;
return this;
}
@@ -532,7 +535,7 @@
return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress,
mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber,
mIsExitedFromAirplaneMode, mImsReasonInfo,
- mCause, mEmergencyRegResult);
+ mCause, mEmergencyRegistrationResult);
}
}
}
@@ -697,7 +700,7 @@
public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
@EmergencyScanType int scanType, boolean resetScan,
@NonNull CancellationSignal signal,
- @NonNull Consumer<EmergencyRegResult> consumer) {
+ @NonNull Consumer<EmergencyRegistrationResult> consumer) {
try {
if (signal != null) signal.setOnCancelListener(this);
mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor);
@@ -721,17 +724,18 @@
private class IWwanSelectorResultCallbackAdapter
extends IWwanSelectorResultCallback.Stub {
- private final @NonNull Consumer<EmergencyRegResult> mConsumer;
+ private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer;
private final @NonNull Executor mExecutor;
- IWwanSelectorResultCallbackAdapter(@NonNull Consumer<EmergencyRegResult> consumer,
+ IWwanSelectorResultCallbackAdapter(
+ @NonNull Consumer<EmergencyRegistrationResult> consumer,
@NonNull Executor executor) {
mConsumer = consumer;
mExecutor = executor;
}
@Override
- public void onComplete(@NonNull EmergencyRegResult result) {
+ public void onComplete(@NonNull EmergencyRegistrationResult result) {
if (mConsumer == null) return;
executeMethodAsyncNoException(mExecutor,
@@ -759,9 +763,8 @@
* @param attr Required to determine the domain.
* @param callback The callback instance being registered.
*/
- public void onDomainSelection(@NonNull SelectionAttributes attr,
- @NonNull TransportSelectorCallback callback) {
- }
+ public abstract void onDomainSelection(@NonNull SelectionAttributes attr,
+ @NonNull TransportSelectorCallback callback);
/**
* Notifies the change in {@link ServiceState} for a specific logical slot index.
@@ -836,7 +839,7 @@
/** @hide */
@Override
- public @Nullable IBinder onBind(@Nullable Intent intent) {
+ public final @Nullable IBinder onBind(@Nullable Intent intent) {
if (intent == null) return null;
if (SERVICE_INTERFACE.equals(intent.getAction())) {
Log.i(LOG_TAG, "DomainSelectionService Bound.");
@@ -863,7 +866,7 @@
* @return {@link Executor} instance.
* @hide
*/
- public @NonNull Executor getCachedExecutor() {
+ public final @NonNull Executor getCachedExecutor() {
synchronized (mExecutorLock) {
if (mExecutor == null) {
Executor e = onCreateExecutor();
diff --git a/telephony/java/android/telephony/EmergencyRegResult.aidl b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl
similarity index 93%
rename from telephony/java/android/telephony/EmergencyRegResult.aidl
rename to telephony/java/android/telephony/EmergencyRegistrationResult.aidl
index f722962..3056031 100644
--- a/telephony/java/android/telephony/EmergencyRegResult.aidl
+++ b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl
@@ -16,4 +16,4 @@
package android.telephony;
-parcelable EmergencyRegResult;
+parcelable EmergencyRegistrationResult;
diff --git a/telephony/java/android/telephony/EmergencyRegResult.java b/telephony/java/android/telephony/EmergencyRegistrationResult.java
similarity index 91%
rename from telephony/java/android/telephony/EmergencyRegResult.java
rename to telephony/java/android/telephony/EmergencyRegistrationResult.java
index 15579be..7041f5b 100644
--- a/telephony/java/android/telephony/EmergencyRegResult.java
+++ b/telephony/java/android/telephony/EmergencyRegistrationResult.java
@@ -35,7 +35,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
-public final class EmergencyRegResult implements Parcelable {
+public final class EmergencyRegistrationResult implements Parcelable {
/**
* Indicates the cellular network type of the acquired system.
@@ -101,7 +101,7 @@
* @param iso The ISO-3166-1 alpha-2 country code equivalent, empty string if unknown.
* @hide
*/
- public EmergencyRegResult(
+ public EmergencyRegistrationResult(
@AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
@NetworkRegistrationInfo.RegistrationState int regState,
@NetworkRegistrationInfo.Domain int domain,
@@ -125,7 +125,7 @@
* @param s Source emergency scan result
* @hide
*/
- public EmergencyRegResult(@NonNull EmergencyRegResult s) {
+ public EmergencyRegistrationResult(@NonNull EmergencyRegistrationResult s) {
mAccessNetworkType = s.mAccessNetworkType;
mRegState = s.mRegState;
mDomain = s.mDomain;
@@ -139,9 +139,9 @@
}
/**
- * Construct a EmergencyRegResult object from the given parcel.
+ * Construct a EmergencyRegistrationResult object from the given parcel.
*/
- private EmergencyRegResult(@NonNull Parcel in) {
+ private EmergencyRegistrationResult(@NonNull Parcel in) {
readFromParcel(in);
}
@@ -258,7 +258,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- EmergencyRegResult that = (EmergencyRegResult) o;
+ EmergencyRegistrationResult that = (EmergencyRegistrationResult) o;
return mAccessNetworkType == that.mAccessNetworkType
&& mRegState == that.mRegState
&& mDomain == that.mDomain
@@ -311,16 +311,16 @@
mCountryIso = in.readString8();
}
- public static final @NonNull Creator<EmergencyRegResult> CREATOR =
- new Creator<EmergencyRegResult>() {
- @Override
- public EmergencyRegResult createFromParcel(@NonNull Parcel in) {
- return new EmergencyRegResult(in);
- }
+ public static final @NonNull Creator<EmergencyRegistrationResult> CREATOR =
+ new Creator<EmergencyRegistrationResult>() {
+ @Override
+ public EmergencyRegistrationResult createFromParcel(@NonNull Parcel in) {
+ return new EmergencyRegistrationResult(in);
+ }
- @Override
- public EmergencyRegResult[] newArray(int size) {
- return new EmergencyRegResult[size];
- }
- };
+ @Override
+ public EmergencyRegistrationResult[] newArray(int size) {
+ return new EmergencyRegistrationResult[size];
+ }
+ };
}
diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java
index ea83815..b900af3 100644
--- a/telephony/java/android/telephony/WwanSelectorCallback.java
+++ b/telephony/java/android/telephony/WwanSelectorCallback.java
@@ -48,7 +48,8 @@
*/
void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
@EmergencyScanType int scanType, boolean resetScan,
- @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegResult> consumer);
+ @NonNull CancellationSignal signal,
+ @NonNull Consumer<EmergencyRegistrationResult> consumer);
/**
* Notifies the FW that the domain has been selected. After this method is called,
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0fe43b3..7935d24 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -23,6 +23,7 @@
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.app.Activity;
import android.app.PendingIntent;
@@ -863,6 +864,10 @@
*/
public static final int ERROR_INVALID_PORT = 10017;
+ /** Temporary failure to retrieve available memory because eUICC is not ready. */
+ @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+ public static final long EUICC_MEMORY_FIELD_UNAVAILABLE = -1L;
+
/**
* Apps targeting on Android T and beyond will get exception whenever switchToSubscription
* without portIndex is called for disable subscription.
@@ -963,6 +968,35 @@
}
/**
+ * Returns the available memory in bytes of the eUICC.
+ *
+ * @return the available memory in bytes. May be {@link #EUICC_MEMORY_FIELD_UNAVAILABLE} if the
+ * eUICC is not ready. Check {@link #isEnabled} for more information.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC} or
+ * device doesn't support querying this information from the eUICC.
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "carrier privileges"
+ })
+ public long getAvailableMemoryInBytes() {
+ if (!isEnabled()) {
+ return EUICC_MEMORY_FIELD_UNAVAILABLE;
+ }
+ try {
+ return getIEuiccController()
+ .getAvailableMemoryInBytes(mCardId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current status of eUICC OTA.
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
diff --git a/telephony/java/android/telephony/satellite/EnableRequestAttributes.java b/telephony/java/android/telephony/satellite/EnableRequestAttributes.java
new file mode 100644
index 0000000..bc9d230
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/EnableRequestAttributes.java
@@ -0,0 +1,145 @@
+/*
+ * 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 android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * EnableRequestAttributes is used to store the attributes of the request
+ * {@link SatelliteManager#requestEnabled(EnableRequestAttributes, Executor, Consumer)}
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public class EnableRequestAttributes {
+ /** {@code true} to enable satellite and {@code false} to disable satellite */
+ private boolean mIsEnabled;
+ /**
+ * {@code true} to enable demo mode and {@code false} to disable. When disabling satellite,
+ * {@code mIsDemoMode} is always considered as {@code false} by Telephony.
+ */
+ private boolean mIsDemoMode;
+ /**
+ * {@code true} means satellite is enabled for emergency mode, {@code false} otherwise. When
+ * disabling satellite, {@code isEmergencyMode} is always considered as {@code false} by
+ * Telephony.
+ */
+ private boolean mIsEmergencyMode;
+
+ /**
+ * Constructor from builder.
+ *
+ * @param builder Builder of {@link EnableRequestAttributes}.
+ */
+ private EnableRequestAttributes(@NonNull Builder builder) {
+ this.mIsEnabled = builder.mIsEnabled;
+ this.mIsDemoMode = builder.mIsDemoMode;
+ this.mIsEmergencyMode = builder.mIsEmergencyMode;
+ }
+
+ /**
+ * @return Whether satellite is to be enabled
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * @return Whether demo mode is to be enabled
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public boolean isDemoMode() {
+ return mIsDemoMode;
+ }
+
+ /**
+ * @return Whether satellite is to be enabled for emergency mode
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public boolean isEmergencyMode() {
+ return mIsEmergencyMode;
+ }
+
+ /**
+ * The builder class of {@link EnableRequestAttributes}
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final class Builder {
+ private boolean mIsEnabled;
+ private boolean mIsDemoMode = false;
+ private boolean mIsEmergencyMode = false;
+
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public Builder(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ }
+
+ /**
+ * Set demo mode
+ *
+ * @param isDemoMode {@code true} to enable demo mode and {@code false} to disable. When
+ * disabling satellite, {@code isDemoMode} is always considered as
+ * {@code false} by Telephony.
+ * @return The builder object
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public Builder setDemoMode(boolean isDemoMode) {
+ if (mIsEnabled) {
+ mIsDemoMode = isDemoMode;
+ }
+ return this;
+ }
+
+ /**
+ * Set emergency mode
+ *
+ * @param isEmergencyMode {@code true} means satellite is enabled for emergency mode,
+ * {@code false} otherwise. When disabling satellite,
+ * {@code isEmergencyMode} is always considered as {@code false} by
+ * Telephony.
+ * @return The builder object
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public Builder setEmergencyMode(boolean isEmergencyMode) {
+ if (mIsEnabled) {
+ mIsEmergencyMode = isEmergencyMode;
+ }
+ return this;
+ }
+
+ /**
+ * Build the {@link EnableRequestAttributes}
+ *
+ * @return The {@link EnableRequestAttributes} instance.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public EnableRequestAttributes build() {
+ return new EnableRequestAttributes(this);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index b97822a..4a61114 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -162,6 +162,13 @@
/**
* Bundle key to get the response from
+ * {@link #requestIsEmergencyModeEnabled(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+ public static final String KEY_EMERGENCY_MODE_ENABLED = "emergency_mode_enabled";
+
+ /**
+ * Bundle key to get the response from
* {@link #requestIsSupported(Executor, OutcomeReceiver)}.
* @hide
*/
@@ -341,6 +348,13 @@
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23;
+ /**
+ * Telephony framework timeout to receive ACK or response from the satellite modem after
+ * sending a request to the modem.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24;
+
/** @hide */
@IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
SATELLITE_RESULT_SUCCESS,
@@ -366,7 +380,8 @@
SATELLITE_RESULT_NOT_SUPPORTED,
SATELLITE_RESULT_REQUEST_IN_PROGRESS,
SATELLITE_RESULT_MODEM_BUSY,
- SATELLITE_RESULT_ILLEGAL_STATE
+ SATELLITE_RESULT_ILLEGAL_STATE,
+ SATELLITE_RESULT_MODEM_TIMEOUT
})
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteResult {}
@@ -482,20 +497,18 @@
* aligned with the satellite, user can send a message and also receive a reply in demo mode.
* If enableSatellite is {@code false}, enableDemoMode has no impact on the behavior.
*
- * @param enableSatellite {@code true} to enable the satellite modem and
- * {@code false} to disable.
- * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
+ * @param attributes The attributes of the enable request.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteResult} result of the operation.
*
* @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 requestEnabled(boolean enableSatellite, boolean enableDemoMode,
+ public void requestEnabled(@NonNull EnableRequestAttributes attributes,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ Objects.requireNonNull(attributes);
Objects.requireNonNull(executor);
Objects.requireNonNull(resultListener);
@@ -509,14 +522,17 @@
() -> resultListener.accept(result)));
}
};
- telephony.requestSatelliteEnabled(mSubId, enableSatellite, enableDemoMode,
- errorCallback);
+ telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(),
+ attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback);
} else {
- throw new IllegalStateException("telephony service is null.");
+ Rlog.e(TAG, "requestEnabled() invalid telephony");
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
- Rlog.e(TAG, "requestSatelliteEnabled() RemoteException: ", ex);
- ex.rethrowAsRuntimeException();
+ Rlog.e(TAG, "requestEnabled() exception: ", ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -566,12 +582,14 @@
};
telephony.requestIsSatelliteEnabled(mSubId, receiver);
} else {
+ loge("requestIsEnabled() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestIsSatelliteEnabled() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("requestIsEnabled() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -621,11 +639,68 @@
};
telephony.requestIsDemoModeEnabled(mSubId, receiver);
} else {
+ loge("requestIsDemoModeEnabled() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
loge("requestIsDemoModeEnabled() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ }
+
+ /**
+ * Request to get whether the satellite service is enabled for emergency mode.
+ *
+ * @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)}
+ * will return a {@code boolean} with value {@code true} if satellite is enabled
+ * for emergency mode and {@code false} otherwise.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+ * will return a {@link SatelliteException} with the {@link SatelliteResult}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void requestIsEmergencyModeEnabled(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData.containsKey(KEY_EMERGENCY_MODE_ENABLED)) {
+ boolean isEmergencyModeEnabled =
+ resultData.getBoolean(KEY_EMERGENCY_MODE_ENABLED);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(isEmergencyModeEnabled)));
+ } else {
+ loge("KEY_EMERGENCY_MODE_ENABLED does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestIsEmergencyModeEnabled(mSubId, receiver);
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+ }
+ } catch (RemoteException ex) {
+ loge("requestIsEmergencyModeEnabled() RemoteException: " + ex);
ex.rethrowAsRuntimeException();
}
}
@@ -678,12 +753,14 @@
};
telephony.requestIsSatelliteSupported(mSubId, receiver);
} else {
+ loge("requestIsSupported() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestIsSatelliteSupported() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("requestIsSupported() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -733,12 +810,14 @@
};
telephony.requestSatelliteCapabilities(mSubId, receiver);
} else {
+ loge("requestCapabilities() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestSatelliteCapabilities() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("requestCapabilities() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -1014,12 +1093,14 @@
telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
internalCallback);
} else {
+ loge("startTransmissionUpdates() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
- loge("startSatelliteTransmissionUpdates() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("startTransmissionUpdates() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1069,12 +1150,14 @@
() -> resultListener.accept(SATELLITE_RESULT_INVALID_ARGUMENTS)));
}
} else {
+ loge("stopTransmissionUpdates() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
- loge("stopSatelliteTransmissionUpdates() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("stopTransmissionUpdates() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1092,7 +1175,6 @@
* @param resultListener Listener for the {@link SatelliteResult} result of the operation.
*
* @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)
@@ -1119,12 +1201,14 @@
cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
errorCallback);
} else {
+ loge("provisionService() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
- loge("provisionSatelliteService() RemoteException=" + ex);
- ex.rethrowAsRuntimeException();
+ loge("provisionService() RemoteException=" + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(cancelRemote);
@@ -1168,12 +1252,14 @@
};
telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
} else {
+ loge("deprovisionService() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
- loge("deprovisionSatelliteService() RemoteException=" + ex);
- ex.rethrowAsRuntimeException();
+ loge("deprovisionService() RemoteException ex=" + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1215,7 +1301,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- loge("registerForSatelliteProvisionStateChanged() RemoteException: " + ex);
+ loge("registerForProvisionStateChanged() RemoteException: " + ex);
ex.rethrowAsRuntimeException();
}
return SATELLITE_RESULT_REQUEST_FAILED;
@@ -1302,12 +1388,14 @@
};
telephony.requestIsSatelliteProvisioned(mSubId, receiver);
} else {
+ loge("requestIsProvisioned() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ loge("requestIsProvisioned() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -1347,7 +1435,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- loge("registerForSatelliteModemStateChanged() RemoteException:" + ex);
+ loge("registerForModemStateChanged() RemoteException:" + ex);
ex.rethrowAsRuntimeException();
}
return SATELLITE_RESULT_REQUEST_FAILED;
@@ -1516,12 +1604,14 @@
};
telephony.pollPendingDatagrams(mSubId, internalCallback);
} else {
+ loge("pollPendingDatagrams() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
loge("pollPendingDatagrams() RemoteException:" + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1573,12 +1663,14 @@
telephony.sendDatagram(mSubId, datagramType, datagram,
needFullScreenPointingUI, internalCallback);
} else {
+ loge("sendDatagram() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
loge("sendDatagram() RemoteException:" + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1628,16 +1720,16 @@
}
}
};
- telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId,
- receiver);
+ telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, receiver);
} else {
+ loge("requestIsCommunicationAllowedForCurrentLocation() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: "
- + ex);
- ex.rethrowAsRuntimeException();
+ loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " + ex);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -1688,12 +1780,14 @@
};
telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver);
} else {
+ loge("requestTimeForNextSatelliteVisibility() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
loge("requestTimeForNextSatelliteVisibility() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -1720,7 +1814,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- loge("informDeviceAlignedToSatellite() RemoteException:" + ex);
+ loge("setDeviceAlignedWithSatellite() RemoteException:" + ex);
ex.rethrowAsRuntimeException();
}
}
@@ -1830,12 +1924,14 @@
};
telephony.addAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
+ loge("addAttachRestrictionForCarrier() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
loge("addAttachRestrictionForCarrier() RemoteException:" + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1873,12 +1969,14 @@
};
telephony.removeAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
+ loge("removeAttachRestrictionForCarrier() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
} catch (RemoteException ex) {
loge("removeAttachRestrictionForCarrier() RemoteException:" + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
}
}
@@ -1939,10 +2037,7 @@
* The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no
* signal strength data available.
* If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a
- * {@link SatelliteException} with the {@link SatelliteResult}, or return a
- * {@link IllegalStateException} if the Telephony process is not currently available or
- * satellite is not supported, or return a {@link RuntimeException} when remote procedure call
- * has failed.
+ * {@link SatelliteException} with the {@link SatelliteResult}.
*
* @throws SecurityException if the caller doesn't have required permission.
*/
@@ -1980,12 +2075,14 @@
};
telephony.requestNtnSignalStrength(mSubId, receiver);
} else {
+ loge("requestNtnSignalStrength() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
loge("requestNtnSignalStrength() RemoteException: " + ex);
- ex.rethrowAsRuntimeException();
+ executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+ new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
}
@@ -2187,14 +2284,11 @@
return new ArrayList<>();
}
- private static ITelephony getITelephony() {
+ @Nullable private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
.getTelephonyServiceManager()
.getTelephonyServiceRegisterer()
.get());
- if (binder == null) {
- throw new RuntimeException("Could not find Telephony Service.");
- }
return binder;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 213fbc5..bd47b1f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2742,14 +2742,19 @@
* Request to enable or disable the satellite modem.
*
* @param subId The subId of the subscription to enable or disable the satellite modem for.
- * @param enable True to enable the satellite modem and false to disable.
- * @param isDemoModeEnabled True if demo mode is enabled and false otherwise.
+ * @param enableSatellite True to enable the satellite modem and false to disable.
+ * @param enableDemoMode True if demo mode is enabled and false otherwise. When
+ * disabling satellite, {@code enableDemoMode} is always considered as
+ * {@code false} by Telephony.
+ * @param isEmergency {@code true} means satellite is enabled for emergency mode, {@code false}
+ * otherwise. When disabling satellite, {@code isEmergency} is always
+ * considered as {@code false} by Telephony.
* @param callback The callback to get the result of the request.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestSatelliteEnabled(int subId, boolean enable, boolean isDemoModeEnabled,
- in IIntegerConsumer callback);
+ void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+ boolean isEmergency, in IIntegerConsumer callback);
/**
* Request to get whether the satellite modem is enabled.
@@ -2775,6 +2780,18 @@
void requestIsDemoModeEnabled(int subId, in ResultReceiver receiver);
/**
+ * Request to get whether the satellite service is enabled with emergency mode.
+ *
+ * @param subId The subId of the subscription to request whether the satellite demo mode is
+ * enabled for.
+ * @param receiver Result receiver to get the error code of the request and whether the
+ * satellite is enabled with emergency mode.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestIsEmergencyModeEnabled(int subId, in ResultReceiver receiver);
+
+ /**
* Request to get whether the satellite service is supported on the device.
*
* @param subId The subId of the subscription to check whether satellite is supported for.
diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
index 0d61fcb..091974a 100644
--- a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
@@ -16,8 +16,8 @@
package com.android.internal.telephony;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
oneway interface IWwanSelectorResultCallback {
- void onComplete(in EmergencyRegResult result);
+ void onComplete(in EmergencyRegistrationResult result);
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index d417772..053bc7d 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -59,4 +59,5 @@
boolean isCompatChangeEnabled(String callingPackage, long changeId);
void setPsimConversionSupportedCarriers(in int[] carrierIds);
boolean isPsimConversionSupported(in int carrierId);
+ long getAvailableMemoryInBytes(int cardId, String callingPackage);
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 73cc2f2..f628af1 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -340,6 +340,14 @@
wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify()
}
+ open fun tapPipToShowMenu(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ }
+
/** Close the pip window by pressing the expand button */
fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
val windowRect = getWindowRect(wmHelper)