diff options
216 files changed, 2592 insertions, 1252 deletions
diff --git a/Android.bp b/Android.bp index 49386d44ede3..895ef98c5537 100644 --- a/Android.bp +++ b/Android.bp @@ -399,6 +399,7 @@ java_defaults { "soundtrigger_middleware-aidl-java", "modules-utils-binary-xml", "modules-utils-build", + "modules-utils-fastxmlserializer", "modules-utils-preconditions", "modules-utils-statemachine", "modules-utils-synchronous-result-receiver", diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index 887f7fe3a0e2..6c83add3b8e3 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -30,6 +30,10 @@ java_library { "unsupportedappusage", ], + static_libs: [ + "modules-utils-fastxmlserializer", + ], + // Rename classes shared with the framework jarjar_rules: "jarjar-rules.txt", diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 7d3837786be9..12f455ad0144 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -2539,6 +2539,38 @@ public class AppStandbyController pw.println("]"); pw.println(); + pw.println("mActiveAdminApps=["); + synchronized (mActiveAdminApps) { + final int size = mActiveAdminApps.size(); + for (int i = 0; i < size; ++i) { + final int userId = mActiveAdminApps.keyAt(i); + pw.print(" "); + pw.print(userId); + pw.print(": "); + pw.print(mActiveAdminApps.valueAt(i)); + if (i != size - 1) pw.print(","); + pw.println(); + } + } + pw.println("]"); + pw.println(); + + pw.println("mAdminProtectedPackages=["); + synchronized (mAdminProtectedPackages) { + final int size = mAdminProtectedPackages.size(); + for (int i = 0; i < size; ++i) { + final int userId = mAdminProtectedPackages.keyAt(i); + pw.print(" "); + pw.print(userId); + pw.print(": "); + pw.print(mAdminProtectedPackages.valueAt(i)); + if (i != size - 1) pw.print(","); + pw.println(); + } + } + pw.println("]"); + pw.println(); + mInjector.dump(pw); } diff --git a/cmds/gpu_counter_producer/main.cpp b/cmds/gpu_counter_producer/main.cpp index 1054cba74a6b..4616638379e2 100644 --- a/cmds/gpu_counter_producer/main.cpp +++ b/cmds/gpu_counter_producer/main.cpp @@ -133,6 +133,12 @@ int main(int argc, char** argv) { daemon(0, 0); } + if (getenv("LD_LIBRARY_PATH") == nullptr) { + setenv("LD_LIBRARY_PATH", "/vendor/lib64:/vendor/lib", 0 /*override*/); + LOG_INFO("execv with: LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH")); + execvpe(pname, argv, environ); + } + if (!writeToPidFile()) { LOG_ERR("Could not open %s", kPidFileName); return 1; diff --git a/core/api/current.txt b/core/api/current.txt index 87f5b3c1cb3b..14869dba9646 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5315,11 +5315,15 @@ package android.app { method public android.net.Uri getConditionId(); method @Nullable public android.content.ComponentName getConfigurationActivity(); method public long getCreationTime(); + method @FlaggedApi("android.app.modes_api") @DrawableRes public int getIconResId(); method public int getInterruptionFilter(); method public String getName(); method public android.content.ComponentName getOwner(); + method @FlaggedApi("android.app.modes_api") @Nullable public String getTriggerDescription(); + method @FlaggedApi("android.app.modes_api") public int getType(); method public android.service.notification.ZenPolicy getZenPolicy(); method public boolean isEnabled(); + method @FlaggedApi("android.app.modes_api") public boolean isManualInvocationAllowed(); method public void setConditionId(android.net.Uri); method public void setConfigurationActivity(@Nullable android.content.ComponentName); method public void setEnabled(boolean); @@ -5328,6 +5332,32 @@ package android.app { method public void setZenPolicy(android.service.notification.ZenPolicy); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; + field @FlaggedApi("android.app.modes_api") public static final int TYPE_BEDTIME = 3; // 0x3 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_DRIVING = 4; // 0x4 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_IMMERSIVE = 5; // 0x5 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_MANAGED = 7; // 0x7 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_OTHER = 0; // 0x0 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_CALENDAR = 2; // 0x2 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_TIME = 1; // 0x1 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_THEATER = 6; // 0x6 + field @FlaggedApi("android.app.modes_api") public static final int TYPE_UNKNOWN = -1; // 0xffffffff + } + + @FlaggedApi("android.app.modes_api") public static final class AutomaticZenRule.Builder { + ctor public AutomaticZenRule.Builder(@NonNull android.app.AutomaticZenRule); + ctor public AutomaticZenRule.Builder(@NonNull String, @NonNull android.net.Uri); + method @NonNull public android.app.AutomaticZenRule build(); + method @NonNull public android.app.AutomaticZenRule.Builder setConditionId(@NonNull android.net.Uri); + method @NonNull public android.app.AutomaticZenRule.Builder setConfigurationActivity(@Nullable android.content.ComponentName); + method @NonNull public android.app.AutomaticZenRule.Builder setEnabled(boolean); + method @NonNull public android.app.AutomaticZenRule.Builder setIconResId(@DrawableRes int); + method @NonNull public android.app.AutomaticZenRule.Builder setInterruptionFilter(int); + method @NonNull public android.app.AutomaticZenRule.Builder setManualInvocationAllowed(boolean); + method @NonNull public android.app.AutomaticZenRule.Builder setName(@NonNull String); + method @NonNull public android.app.AutomaticZenRule.Builder setOwner(@Nullable android.content.ComponentName); + method @NonNull public android.app.AutomaticZenRule.Builder setTriggerDescription(@Nullable String); + method @NonNull public android.app.AutomaticZenRule.Builder setType(int); + method @NonNull public android.app.AutomaticZenRule.Builder setZenPolicy(@Nullable android.service.notification.ZenPolicy); } public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b2bfda1daf41..8b20720418b6 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3592,8 +3592,8 @@ package android.view { field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000 field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000 field public CharSequence accessibilityTitle; - field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate; - field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate; + field public float preferredMaxDisplayRefreshRate; + field public float preferredMinDisplayRefreshRate; field public int privateFlags; } diff --git a/core/java/Android.bp b/core/java/Android.bp index ddb221f422d9..48cafc596d87 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -226,7 +226,6 @@ filegroup { "com/android/internal/util/ConcurrentUtils.java", "com/android/internal/util/DumpUtils.java", "com/android/internal/util/FastPrintWriter.java", - "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/FunctionalUtils.java", "com/android/internal/util/ParseUtils.java", "com/android/internal/util/RingBufferIndices.java", @@ -465,7 +464,6 @@ filegroup { "com/android/internal/util/AsyncChannel.java", "com/android/internal/util/AsyncService.java", "com/android/internal/util/BitwiseInputStream.java", - "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/HexDump.java", "com/android/internal/util/IndentingPrintWriter.java", "com/android/internal/util/UserIcons.java", @@ -515,7 +513,6 @@ filegroup { "android/net/InterfaceConfiguration.java", "android/util/BackupUtils.java", "android/util/Rational.java", - "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/HexDump.java", "com/android/internal/util/MessageUtils.java", "com/android/internal/util/WakeupMessage.java", diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b6ea14ab0b6..c136db68fd25 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -378,6 +378,15 @@ public final class ActivityThread extends ClientTransactionHandler /** Maps from activity token to the pending override configuration. */ @GuardedBy("mPendingOverrideConfigs") private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>(); + + /** + * A queue of pending ApplicationInfo updates. In case when we get a concurrent update + * this queue allows us to only apply the latest object, and it can be applied on demand + * instead of waiting for the handler thread to reach the scheduled callback. + */ + @GuardedBy("mResourcesManager") + private final ArrayMap<String, ApplicationInfo> mPendingAppInfoUpdates = new ArrayMap<>(); + /** The activities to be truly destroyed (not include relaunch). */ final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed = Collections.synchronizedMap(new ArrayMap<>()); @@ -1326,9 +1335,19 @@ public final class ActivityThread extends ClientTransactionHandler } public void scheduleApplicationInfoChanged(ApplicationInfo ai) { + synchronized (mResourcesManager) { + var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai); + if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) { + Slog.w(TAG, "Skipping application info changed for obsolete AI with TS " + + ai.createTimestamp + " < already pending TS " + + oldAi.createTimestamp); + mPendingAppInfoUpdates.put(ai.packageName, oldAi); + return; + } + } mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai); - mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai); - sendMessage(H.APPLICATION_INFO_CHANGED, ai); + mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName); + sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName); } public void updateTimeZone() { @@ -2507,7 +2526,7 @@ public final class ActivityThread extends ClientTransactionHandler break; } case APPLICATION_INFO_CHANGED: - handleApplicationInfoChanged((ApplicationInfo) msg.obj); + applyPendingApplicationInfoChanges((String) msg.obj); break; case RUN_ISOLATED_ENTRY_POINT: handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1, @@ -4070,7 +4089,8 @@ public final class ActivityThread extends ClientTransactionHandler mProfiler.startProfiling(); } - // Make sure we are running with the most recent config. + // Make sure we are running with the most recent config and resource paths. + applyPendingApplicationInfoChanges(r.activityInfo.packageName); mConfigurationController.handleConfigurationChanged(null, null); updateDeviceIdForNonUIContexts(deviceId); @@ -6438,6 +6458,17 @@ public final class ActivityThread extends ClientTransactionHandler r.mLastReportedWindowingMode = newWindowingMode; } + private void applyPendingApplicationInfoChanges(String packageName) { + final ApplicationInfo ai; + synchronized (mResourcesManager) { + ai = mPendingAppInfoUpdates.remove(packageName); + } + if (ai == null) { + return; + } + handleApplicationInfoChanged(ai); + } + /** * Updates the application info. * @@ -6463,6 +6494,16 @@ public final class ActivityThread extends ClientTransactionHandler apk = ref != null ? ref.get() : null; ref = mResourcePackages.get(ai.packageName); resApk = ref != null ? ref.get() : null; + for (ActivityClientRecord ar : mActivities.values()) { + if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) { + ar.activityInfo.applicationInfo = ai; + if (apk != null || resApk != null) { + ar.packageInfo = apk != null ? apk : resApk; + } else { + apk = ar.packageInfo; + } + } + } } if (apk != null) { diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 7bfb1b5c1ba6..919e084002ea 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -16,16 +16,23 @@ package android.app; +import android.annotation.DrawableRes; +import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.NotificationManager.InterruptionFilter; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.service.notification.Condition; import android.service.notification.ZenPolicy; +import android.view.WindowInsetsController; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -36,7 +43,67 @@ public final class AutomaticZenRule implements Parcelable { private static final int ENABLED = 1; /* @hide */ private static final int DISABLED = 0; - private boolean enabled = false; + + /** + * Rule is of an unknown type. This is the default value if not provided by the owning app. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_UNKNOWN = -1; + /** + * Rule is of a known type, but not one of the specific types. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_OTHER = 0; + /** + * The type for rules triggered according to a time-based schedule. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_SCHEDULE_TIME = 1; + /** + * The type for rules triggered by calendar events. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_SCHEDULE_CALENDAR = 2; + /** + * The type for rules triggered by bedtime/sleeping, like time of day, or snore detection. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_BEDTIME = 3; + /** + * The type for rules triggered by driving detection, like Bluetooth connections or vehicle + * sounds. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_DRIVING = 4; + /** + * The type for rules triggered by the user entering an immersive activity, like opening an app + * using {@link WindowInsetsController#hide(int)}. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_IMMERSIVE = 5; + /** + * The type for rules that have a {@link ZenPolicy} that implies that the + * device should not make sound and potentially hide some visual effects; may be triggered + * when entering a location where silence is requested, like a theater. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_THEATER = 6; + /** + * The type for rules created and managed by a device owner. These rules may not be fully + * editable by the device user. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int TYPE_MANAGED = 7; + + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME, + TYPE_DRIVING, TYPE_IMMERSIVE, TYPE_THEATER, TYPE_MANAGED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + private boolean enabled; private String name; private @InterruptionFilter int interruptionFilter; private Uri conditionId; @@ -46,6 +113,10 @@ public final class AutomaticZenRule implements Parcelable { private ZenPolicy mZenPolicy; private boolean mModified = false; private String mPkg; + private int mType = TYPE_UNKNOWN; + private int mIconResId; + private String mTriggerDescription; + private boolean mAllowManualInvocation; /** * The maximum string length for any string contained in this automatic zen rule. This pertains @@ -55,6 +126,12 @@ public final class AutomaticZenRule implements Parcelable { public static final int MAX_STRING_LENGTH = 1000; /** + * The maximum string length for the trigger description rule, given UI constraints. + * @hide + */ + public static final int MAX_DESC_LENGTH = 150; + + /** * Creates an automatic zen rule. * * @param name The name of the rule. @@ -97,6 +174,7 @@ public final class AutomaticZenRule implements Parcelable { * action ({@link Condition#STATE_TRUE}). * @param enabled Whether the rule is enabled. */ + // TODO (b/309088420): deprecate this constructor in favor of the builder public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner, @Nullable ComponentName configurationActivity, @NonNull Uri conditionId, @Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) { @@ -134,6 +212,12 @@ public final class AutomaticZenRule implements Parcelable { mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); mModified = source.readInt() == ENABLED; mPkg = source.readString(); + if (Flags.modesApi()) { + mAllowManualInvocation = source.readBoolean(); + mIconResId = source.readInt(); + mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH); + mType = source.readInt(); + } } /** @@ -269,6 +353,81 @@ public final class AutomaticZenRule implements Parcelable { return mPkg; } + /** + * Gets the type of the rule. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public int getType() { + return mType; + } + + /** + * Sets the type of the rule. + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void setType(@Type int type) { + mType = type; + } + + /** + * Gets the user visible description of when this rule is active + * (see {@link Condition#STATE_TRUE}). + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public @Nullable String getTriggerDescription() { + return mTriggerDescription; + } + + /** + * Sets a user visible description of when this rule will be active + * (see {@link Condition#STATE_TRUE}). + * + * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or + * "When connected to [Car Name]". + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void setTriggerDescription(@Nullable String triggerDescription) { + mTriggerDescription = triggerDescription; + } + + /** + * Gets the resource id of the drawable icon for this rule. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public @DrawableRes int getIconResId() { + return mIconResId; + } + + /** + * Sets a resource id of a tintable vector drawable representing the rule in image form. + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void setIconResId(int iconResId) { + mIconResId = iconResId; + } + + /** + * Gets whether this rule can be manually activated by the user even when the triggering + * condition for the rule is not met. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public boolean isManualInvocationAllowed() { + return mAllowManualInvocation; + } + + /** + * Sets whether this rule can be manually activated by the user even when the triggering + * condition for the rule is not met. + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void setManualInvocationAllowed(boolean allowManualInvocation) { + mAllowManualInvocation = allowManualInvocation; + } + @Override public int describeContents() { return 0; @@ -291,11 +450,17 @@ public final class AutomaticZenRule implements Parcelable { dest.writeParcelable(mZenPolicy, 0); dest.writeInt(mModified ? ENABLED : DISABLED); dest.writeString(mPkg); + if (Flags.modesApi()) { + dest.writeBoolean(mAllowManualInvocation); + dest.writeInt(mIconResId); + dest.writeString(mTriggerDescription); + dest.writeInt(mType); + } } @Override public String toString() { - return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[') + StringBuilder sb = new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[') .append("enabled=").append(enabled) .append(",name=").append(name) .append(",interruptionFilter=").append(interruptionFilter) @@ -304,8 +469,16 @@ public final class AutomaticZenRule implements Parcelable { .append(",owner=").append(owner) .append(",configActivity=").append(configurationActivity) .append(",creationTime=").append(creationTime) - .append(",mZenPolicy=").append(mZenPolicy) - .append(']').toString(); + .append(",mZenPolicy=").append(mZenPolicy); + + if (Flags.modesApi()) { + sb.append(",allowManualInvocation=").append(mAllowManualInvocation) + .append(",iconResId=").append(mIconResId) + .append(",triggerDescription=").append(mTriggerDescription) + .append(",type=").append(mType); + } + + return sb.append(']').toString(); } @Override @@ -313,7 +486,7 @@ public final class AutomaticZenRule implements Parcelable { if (!(o instanceof AutomaticZenRule)) return false; if (o == this) return true; final AutomaticZenRule other = (AutomaticZenRule) o; - return other.enabled == enabled + boolean finalEquals = other.enabled == enabled && other.mModified == mModified && Objects.equals(other.name, name) && other.interruptionFilter == interruptionFilter @@ -323,10 +496,23 @@ public final class AutomaticZenRule implements Parcelable { && Objects.equals(other.configurationActivity, configurationActivity) && Objects.equals(other.mPkg, mPkg) && other.creationTime == creationTime; + if (Flags.modesApi()) { + return finalEquals + && other.mAllowManualInvocation == mAllowManualInvocation + && other.mIconResId == mIconResId + && Objects.equals(other.mTriggerDescription, mTriggerDescription) + && other.mType == mType; + } + return finalEquals; } @Override public int hashCode() { + if (Flags.modesApi()) { + return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, + configurationActivity, mZenPolicy, mModified, creationTime, mPkg, + mAllowManualInvocation, mIconResId, mTriggerDescription, mType); + } return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, configurationActivity, mZenPolicy, mModified, creationTime, mPkg); } @@ -357,8 +543,12 @@ public final class AutomaticZenRule implements Parcelable { * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH. */ private static String getTrimmedString(String input) { - if (input != null && input.length() > MAX_STRING_LENGTH) { - return input.substring(0, MAX_STRING_LENGTH); + return getTrimmedString(input, MAX_STRING_LENGTH); + } + + private static String getTrimmedString(String input, int length) { + if (input != null && input.length() > length) { + return input.substring(0, length); } return input; } @@ -373,4 +563,138 @@ public final class AutomaticZenRule implements Parcelable { } return input; } + + @FlaggedApi(Flags.FLAG_MODES_API) + public static final class Builder { + private String mName; + private ComponentName mOwner; + private Uri mConditionId; + private int mInterruptionFilter; + private boolean mEnabled; + private ComponentName mConfigurationActivity = null; + private ZenPolicy mPolicy = null; + private int mType; + private String mDescription; + private int mIconResId; + private boolean mAllowManualInvocation; + private long mCreationTime; + private String mPkg; + + public Builder(@NonNull AutomaticZenRule rule) { + mName = rule.getName(); + mOwner = rule.getOwner(); + mConditionId = rule.getConditionId(); + mInterruptionFilter = rule.getInterruptionFilter(); + mEnabled = rule.isEnabled(); + mConfigurationActivity = rule.getConfigurationActivity(); + mPolicy = rule.getZenPolicy(); + mType = rule.getType(); + mDescription = rule.getTriggerDescription(); + mIconResId = rule.getIconResId(); + mAllowManualInvocation = rule.isManualInvocationAllowed(); + mCreationTime = rule.getCreationTime(); + mPkg = rule.getPackageName(); + } + + public Builder(@NonNull String name, @NonNull Uri conditionId) { + mName = name; + mConditionId = conditionId; + } + + public @NonNull Builder setName(@NonNull String name) { + mName = name; + return this; + } + + public @NonNull Builder setOwner(@Nullable ComponentName owner) { + mOwner = owner; + return this; + } + + public @NonNull Builder setConditionId(@NonNull Uri conditionId) { + mConditionId = conditionId; + return this; + } + + public @NonNull Builder setInterruptionFilter( + @InterruptionFilter int interruptionFilter) { + mInterruptionFilter = interruptionFilter; + return this; + } + + public @NonNull Builder setEnabled(boolean enabled) { + mEnabled = enabled; + return this; + } + + public @NonNull Builder setConfigurationActivity( + @Nullable ComponentName configurationActivity) { + mConfigurationActivity = configurationActivity; + return this; + } + + public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) { + mPolicy = policy; + return this; + } + + /** + * Sets the type of the rule + */ + public @NonNull Builder setType(@Type int type) { + mType = type; + return this; + } + + /** + * Sets a user visible description of when this rule will be active + * (see {@link Condition#STATE_TRUE}). + * + * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or + * "When connected to [Car Name]". + */ + public @NonNull Builder setTriggerDescription(@Nullable String description) { + mDescription = description; + return this; + } + + /** + * Sets a resource id of a tintable vector drawable representing the rule in image form. + */ + public @NonNull Builder setIconResId(@DrawableRes int iconResId) { + mIconResId = iconResId; + return this; + } + + /** + * Sets whether this rule can be manually activated by the user even when the triggering + * condition for the rule is not met. + */ + public @NonNull Builder setManualInvocationAllowed(boolean allowManualInvocation) { + mAllowManualInvocation = allowManualInvocation; + return this; + } + + /** + * Sets the time at which this rule was created, in milliseconds since epoch + * @hide + */ + public @NonNull Builder setCreationTime(long creationTime) { + mCreationTime = creationTime; + return this; + } + + public @NonNull AutomaticZenRule build() { + AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity, + mConditionId, mPolicy, mInterruptionFilter, mEnabled); + rule.creationTime = mCreationTime; + rule.mType = mType; + rule.mTriggerDescription = mDescription; + rule.mIconResId = mIconResId; + rule.mAllowManualInvocation = mAllowManualInvocation; + rule.setPackageName(mPkg); + + return rule; + } + } } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index ebf183ff18c3..f1e44cc267d6 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -342,7 +342,9 @@ public final class LoadedApk { */ public void updateApplicationInfo(@NonNull ApplicationInfo aInfo, @Nullable List<String> oldPaths) { - setApplicationInfo(aInfo); + if (!setApplicationInfo(aInfo)) { + return; + } final List<String> newPaths = new ArrayList<>(); makePaths(mActivityThread, aInfo, newPaths); @@ -387,7 +389,13 @@ public final class LoadedApk { mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader); } - private void setApplicationInfo(ApplicationInfo aInfo) { + private boolean setApplicationInfo(ApplicationInfo aInfo) { + if (mApplicationInfo != null && mApplicationInfo.createTimestamp > aInfo.createTimestamp) { + Slog.w(TAG, "New application info for package " + aInfo.packageName + + " is out of date with TS " + aInfo.createTimestamp + " < the current TS " + + mApplicationInfo.createTimestamp); + return false; + } final int myUid = Process.myUid(); aInfo = adjustNativeLibraryPaths(aInfo); mApplicationInfo = aInfo; @@ -410,6 +418,7 @@ public final class LoadedApk { if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) { mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies); } + return true; } void setSdkSandboxStorage(@Nullable String sdkSandboxClientAppVolumeUuid, diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING index 780904861f0c..49a4467b1f67 100644 --- a/core/java/android/app/time/TEST_MAPPING +++ b/core/java/android/app/time/TEST_MAPPING @@ -1,24 +1,11 @@ { - "presubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.time." - } - ] - } - ], // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. "postsubmit": [ { - "name": "FrameworksCoreTests", + "name": "FrameworksTimeCoreTests", "options": [ { - "include-filter": "android.app.timedetector." - }, - { - "include-filter": "android.app.timezonedetector." + "include-filter": "android.app." } ] }, diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING index 53fd74bc3c3a..c050a55a3e18 100644 --- a/core/java/android/app/timedetector/TEST_MAPPING +++ b/core/java/android/app/timedetector/TEST_MAPPING @@ -2,13 +2,10 @@ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. "postsubmit": [ { - "name": "FrameworksCoreTests", + "name": "FrameworksTimeCoreTests", "options": [ { - "include-filter": "android.app.time." - }, - { - "include-filter": "android.app.timedetector." + "include-filter": "android.app." } ] }, diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING index 5e64c83937f6..46656d125b70 100644 --- a/core/java/android/app/timezonedetector/TEST_MAPPING +++ b/core/java/android/app/timezonedetector/TEST_MAPPING @@ -1,21 +1,11 @@ { - "presubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.timezonedetector." - } - ] - } - ], // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. "postsubmit": [ { - "name": "FrameworksCoreTests", + "name": "FrameworksTimeCoreTests", "options": [ { - "include-filter": "android.app.time." + "include-filter": "android.app." } ] }, diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 43cf97fd2090..9ec082ab8eea 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -35,3 +35,10 @@ flag { description: "Further framework support for communal profile, beyond the basics, for later releases." bug: "285426179" } + +flag { + name: "use_all_cpus_during_user_switch" + namespace: "multiuser" + description: "Allow using all cpu cores during a user switch." + bug: "308105403" +} diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 47b6d8d6db30..94f90ccb2d9d 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -2018,9 +2018,13 @@ public final class StrictMode { return; } + // Temporarily disable checks so that explicit GC is allowed. + final int oldMask = getThreadPolicyMask(); + setThreadPolicyMask(0); System.gc(); System.runFinalization(); System.gc(); + setThreadPolicyMask(oldMask); // Note: classInstanceLimit is immutable, so this is lock-free // Create the classes array. diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 27ad45de69e6..bcda25a1bf3b 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3206,6 +3206,15 @@ public final class Telephony { public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask"; /** + * Indicating if the APN is used for eSIM bootsrap provisioning. The default value is 0 (Not + * used for eSIM bootstrap provisioning). + * + * <P>Type: INTEGER</P> + * @hide + */ + public static final String ESIM_BOOTSTRAP_PROVISIONING = "esim_bootstrap_provisioning"; + + /** * MVNO type: * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}. * <P>Type: TEXT</P> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 828c062d955d..ff4dfc7cc079 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -28,6 +28,8 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OF import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AlarmManager; +import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.compat.annotation.UnsupportedAppUsage; @@ -177,6 +179,10 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_CREATION_TIME = "creationTime"; private static final String RULE_ATT_ENABLER = "enabler"; private static final String RULE_ATT_MODIFIED = "modified"; + private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable"; + private static final String RULE_ATT_TYPE = "type"; + private static final String RULE_ATT_ICON = "rule_icon"; + private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; @UnsupportedAppUsage public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; @@ -212,7 +218,7 @@ public class ZenModeConfig implements Parcelable { allowCallsFrom = source.readInt(); allowMessagesFrom = source.readInt(); user = source.readInt(); - manualRule = source.readParcelable(null, android.service.notification.ZenModeConfig.ZenRule.class); + manualRule = source.readParcelable(null, ZenRule.class); final int len = source.readInt(); if (len > 0) { final String[] ids = new String[len]; @@ -622,6 +628,12 @@ public class ZenModeConfig implements Parcelable { } rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); rt.zenPolicy = readZenPolicyXml(parser); + if (Flags.modesApi()) { + rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false); + rt.iconResId = safeInt(parser, RULE_ATT_ICON, 0); + rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC); + rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN); + } return rt; } @@ -655,6 +667,14 @@ public class ZenModeConfig implements Parcelable { writeZenPolicyXml(rule.zenPolicy, out); } out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified); + if (Flags.modesApi()) { + out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation); + out.attributeInt(null, RULE_ATT_ICON, rule.iconResId); + if (rule.triggerDescription != null) { + out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription); + } + out.attributeInt(null, RULE_ATT_TYPE, rule.type); + } } public static Condition readConditionXml(TypedXmlPullParser parser) { @@ -1726,6 +1746,11 @@ public class ZenModeConfig implements Parcelable { public ZenPolicy zenPolicy; public boolean modified; // rule has been modified from initial creation public String pkg; + public int type = AutomaticZenRule.TYPE_UNKNOWN; + public String triggerDescription; + // TODO (b/308672670): switch to string res name + public int iconResId; + public boolean allowManualInvocation; public ZenRule() { } @@ -1750,6 +1775,12 @@ public class ZenModeConfig implements Parcelable { zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); modified = source.readInt() == 1; pkg = source.readString(); + if (Flags.modesApi()) { + allowManualInvocation = source.readBoolean(); + iconResId = source.readInt(); + triggerDescription = source.readString(); + type = source.readInt(); + } } @Override @@ -1788,11 +1819,17 @@ public class ZenModeConfig implements Parcelable { dest.writeParcelable(zenPolicy, 0); dest.writeInt(modified ? 1 : 0); dest.writeString(pkg); + if (Flags.modesApi()) { + dest.writeBoolean(allowManualInvocation); + dest.writeInt(iconResId); + dest.writeString(triggerDescription); + dest.writeInt(type); + } } @Override public String toString() { - return new StringBuilder(ZenRule.class.getSimpleName()).append('[') + StringBuilder sb = new StringBuilder(ZenRule.class.getSimpleName()).append('[') .append("id=").append(id) .append(",state=").append(condition == null ? "STATE_FALSE" : Condition.stateToString(condition.state)) @@ -1808,8 +1845,16 @@ public class ZenModeConfig implements Parcelable { .append(",enabler=").append(enabler) .append(",zenPolicy=").append(zenPolicy) .append(",modified=").append(modified) - .append(",condition=").append(condition) - .append(']').toString(); + .append(",condition=").append(condition); + + if (Flags.modesApi()) { + sb.append(",allowManualInvocation=").append(allowManualInvocation) + .append(",iconResId=").append(iconResId) + .append(",triggerDescription=").append(triggerDescription) + .append(",type=").append(type); + } + + return sb.append(']').toString(); } /** @hide */ @@ -1845,7 +1890,7 @@ public class ZenModeConfig implements Parcelable { if (!(o instanceof ZenRule)) return false; if (o == this) return true; final ZenRule other = (ZenRule) o; - return other.enabled == enabled + boolean finalEquals = other.enabled == enabled && other.snoozing == snoozing && Objects.equals(other.name, name) && other.zenMode == zenMode @@ -1858,10 +1903,25 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.zenPolicy, zenPolicy) && Objects.equals(other.pkg, pkg) && other.modified == modified; + + if (Flags.modesApi()) { + return finalEquals + && other.allowManualInvocation == allowManualInvocation + && other.iconResId == iconResId + && Objects.equals(other.triggerDescription, triggerDescription) + && other.type == type; + } + + return finalEquals; } @Override public int hashCode() { + if (Flags.modesApi()) { + return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, + component, configurationActivity, pkg, id, enabler, zenPolicy, modified, + allowManualInvocation, iconResId, triggerDescription, type); + } return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, modified); } diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java index a4f129ec247e..eb55e40f2c7b 100644 --- a/core/java/android/service/notification/ZenModeDiff.java +++ b/core/java/android/service/notification/ZenModeDiff.java @@ -454,6 +454,11 @@ public class ZenModeDiff { public static final String FIELD_ZEN_POLICY = "zenPolicy"; public static final String FIELD_MODIFIED = "modified"; public static final String FIELD_PKG = "pkg"; + public static final String FIELD_ALLOW_MANUAL = "allowManualInvocation"; + public static final String FIELD_ICON_RES = "iconResId"; + public static final String FIELD_TRIGGER = "triggerDescription"; + public static final String FIELD_TYPE = "type"; + // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule // Special field to track whether this rule became active or inactive FieldDiff<Boolean> mActiveDiff; @@ -529,6 +534,20 @@ public class ZenModeDiff { if (!Objects.equals(from.pkg, to.pkg)) { addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg)); } + if (!Objects.equals(from.triggerDescription, to.triggerDescription)) { + addField(FIELD_TRIGGER, + new FieldDiff<>(from.triggerDescription, to.triggerDescription)); + } + if (from.type != to.type) { + addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type)); + } + if (from.allowManualInvocation != to.allowManualInvocation) { + addField(FIELD_ALLOW_MANUAL, + new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation)); + } + if (!Objects.equals(from.iconResId, to.iconResId)) { + addField(FIELD_ICON_RES, new FieldDiff(from.iconResId, to.iconResId)); + } } /** diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING index b0ce1dba3509..21a8eab19837 100644 --- a/core/java/android/service/timezone/TEST_MAPPING +++ b/core/java/android/service/timezone/TEST_MAPPING @@ -2,10 +2,10 @@ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. "postsubmit": [ { - "name": "FrameworksCoreTests", + "name": "FrameworksTimeCoreTests", "options": [ { - "include-filter": "android.service.timezone." + "include-filter": "android.service." } ] }, diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c9c1f20d2f37..d97dfb0f9c94 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -123,6 +123,7 @@ import android.content.res.TypedArray; import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ForceDarkType; import android.graphics.FrameInfo; import android.graphics.HardwareRenderer; import android.graphics.HardwareRenderer.FrameDrawingCallback; @@ -1796,7 +1797,7 @@ public final class ViewRootImpl implements ViewParent, /** Returns true if force dark should be enabled according to various settings */ @VisibleForTesting - public boolean isForceDarkEnabled() { + public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() { if (forceInvertColor()) { boolean isForceInvertEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), @@ -1808,7 +1809,7 @@ public final class ViewRootImpl implements ViewParent, // for dark mode in configuration.uiMode. Instead, we assume that the force invert // setting will be enabled at the same time dark theme is in the Settings app. if (isForceInvertEnabled) { - return true; + return ForceDarkType.FORCE_INVERT_COLOR_DARK; } } @@ -1822,12 +1823,12 @@ public final class ViewRootImpl implements ViewParent, && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); a.recycle(); } - return useAutoDark; + return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE; } private void updateForceDarkMode() { if (mAttachInfo.mThreadedRenderer == null) return; - if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) { + if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) { // TODO: Don't require regenerating all display lists to apply this setting invalidateWorld(mView); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index cfec08120776..c73514250e3b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -17,7 +17,6 @@ package android.view; import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT; -import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST; import static android.view.View.STATUS_BAR_DISABLE_BACK; import static android.view.View.STATUS_BAR_DISABLE_CLOCK; import static android.view.View.STATUS_BAR_DISABLE_EXPAND; @@ -3907,7 +3906,7 @@ public interface WindowManager extends ViewManager { * This value is ignored if {@link #preferredDisplayModeId} is set. * @hide */ - @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST) + @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi public float preferredMinDisplayRefreshRate; @@ -3917,7 +3916,7 @@ public interface WindowManager extends ViewManager { * This value is ignored if {@link #preferredDisplayModeId} is set. * @hide */ - @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST) + @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi public float preferredMaxDisplayRefreshRate; diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index cc951cf46870..a467afe5d06a 100644 --- a/core/java/android/view/flags/refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -42,11 +42,4 @@ flag { namespace: "core_graphics" description: "Enable the `setFrameRate` callback" bug: "299946220" -} - -flag { - name: "wm_display_refresh_rate_test" - namespace: "core_graphics" - description: "Adds WindowManager display refresh rate fields to test API" - bug: "304475199" }
\ No newline at end of file diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 7f0a651c6420..e20357fa2dd9 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -12,6 +12,6 @@ per-file TextView*,Edit*,Selection* = file:../text/OWNERS per-file SpellChecker.java = file:../view/inputmethod/OWNERS -per-file RemoteViews* = file:../appwidget/OWNERS +per-file Remote* = file:../appwidget/OWNERS per-file Toast.java = juliacr@google.com, jeffdq@google.com diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java deleted file mode 100644 index 929c9e8bb8c1..000000000000 --- a/core/java/com/android/internal/util/FastXmlSerializer.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2006 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.internal.util; - -import android.compat.annotation.UnsupportedAppUsage; - -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction; -import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.UnsupportedCharsetException; - -/** - * This is a quick and dirty implementation of XmlSerializer that isn't horribly - * painfully slow like the normal one. It only does what is needed for the - * specific XML files being written with it. - */ -public class FastXmlSerializer implements XmlSerializer { - private static final String ESCAPE_TABLE[] = new String[] { - "�", "", "", "", "", "", "", "", // 0-7 - "", "	", " ", "", "", " ", "", "", // 8-15 - "", "", "", "", "", "", "", "", // 16-23 - "", "", "", "", "", "", "", "", // 24-31 - null, null, """, null, null, null, "&", null, // 32-39 - null, null, null, null, null, null, null, null, // 40-47 - null, null, null, null, null, null, null, null, // 48-55 - null, null, null, null, "<", null, ">", null, // 56-63 - }; - - private static final int DEFAULT_BUFFER_LEN = 32*1024; - - private static String sSpace = " "; - - private final int mBufferLen; - private final char[] mText; - private int mPos; - - private Writer mWriter; - - private OutputStream mOutputStream; - private CharsetEncoder mCharset; - private ByteBuffer mBytes; - - private boolean mIndent = false; - private boolean mInTag; - - private int mNesting = 0; - private boolean mLineStart = true; - - @UnsupportedAppUsage - public FastXmlSerializer() { - this(DEFAULT_BUFFER_LEN); - } - - /** - * Allocate a FastXmlSerializer with the given internal output buffer size. If the - * size is zero or negative, then the default buffer size will be used. - * - * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use. - */ - public FastXmlSerializer(int bufferSize) { - mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN; - mText = new char[mBufferLen]; - mBytes = ByteBuffer.allocate(mBufferLen); - } - - private void append(char c) throws IOException { - int pos = mPos; - if (pos >= (mBufferLen-1)) { - flush(); - pos = mPos; - } - mText[pos] = c; - mPos = pos+1; - } - - private void append(String str, int i, final int length) throws IOException { - if (length > mBufferLen) { - final int end = i + length; - while (i < end) { - int next = i + mBufferLen; - append(str, i, next<end ? mBufferLen : (end-i)); - i = next; - } - return; - } - int pos = mPos; - if ((pos+length) > mBufferLen) { - flush(); - pos = mPos; - } - str.getChars(i, i+length, mText, pos); - mPos = pos + length; - } - - private void append(char[] buf, int i, final int length) throws IOException { - if (length > mBufferLen) { - final int end = i + length; - while (i < end) { - int next = i + mBufferLen; - append(buf, i, next<end ? mBufferLen : (end-i)); - i = next; - } - return; - } - int pos = mPos; - if ((pos+length) > mBufferLen) { - flush(); - pos = mPos; - } - System.arraycopy(buf, i, mText, pos, length); - mPos = pos + length; - } - - private void append(String str) throws IOException { - append(str, 0, str.length()); - } - - private void appendIndent(int indent) throws IOException { - indent *= 4; - if (indent > sSpace.length()) { - indent = sSpace.length(); - } - append(sSpace, 0, indent); - } - - private void escapeAndAppendString(final String string) throws IOException { - final int N = string.length(); - final char NE = (char)ESCAPE_TABLE.length; - final String[] escapes = ESCAPE_TABLE; - int lastPos = 0; - int pos; - for (pos=0; pos<N; pos++) { - char c = string.charAt(pos); - if (c >= NE) continue; - String escape = escapes[c]; - if (escape == null) continue; - if (lastPos < pos) append(string, lastPos, pos-lastPos); - lastPos = pos + 1; - append(escape); - } - if (lastPos < pos) append(string, lastPos, pos-lastPos); - } - - private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { - final char NE = (char)ESCAPE_TABLE.length; - final String[] escapes = ESCAPE_TABLE; - int end = start+len; - int lastPos = start; - int pos; - for (pos=start; pos<end; pos++) { - char c = buf[pos]; - if (c >= NE) continue; - String escape = escapes[c]; - if (escape == null) continue; - if (lastPos < pos) append(buf, lastPos, pos-lastPos); - lastPos = pos + 1; - append(escape); - } - if (lastPos < pos) append(buf, lastPos, pos-lastPos); - } - - public XmlSerializer attribute(String namespace, String name, String value) throws IOException, - IllegalArgumentException, IllegalStateException { - append(' '); - if (namespace != null) { - append(namespace); - append(':'); - } - append(name); - append("=\""); - - escapeAndAppendString(value); - append('"'); - mLineStart = false; - return this; - } - - public void cdsect(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void comment(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void docdecl(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { - flush(); - } - - public XmlSerializer endTag(String namespace, String name) throws IOException, - IllegalArgumentException, IllegalStateException { - mNesting--; - if (mInTag) { - append(" />\n"); - } else { - if (mIndent && mLineStart) { - appendIndent(mNesting); - } - append("</"); - if (namespace != null) { - append(namespace); - append(':'); - } - append(name); - append(">\n"); - } - mLineStart = true; - mInTag = false; - return this; - } - - public void entityRef(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - private void flushBytes() throws IOException { - int position; - if ((position = mBytes.position()) > 0) { - mBytes.flip(); - mOutputStream.write(mBytes.array(), 0, position); - mBytes.clear(); - } - } - - public void flush() throws IOException { - //Log.i("PackageManager", "flush mPos=" + mPos); - if (mPos > 0) { - if (mOutputStream != null) { - CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); - CoderResult result = mCharset.encode(charBuffer, mBytes, true); - while (true) { - if (result.isError()) { - throw new IOException(result.toString()); - } else if (result.isOverflow()) { - flushBytes(); - result = mCharset.encode(charBuffer, mBytes, true); - continue; - } - break; - } - flushBytes(); - mOutputStream.flush(); - } else { - mWriter.write(mText, 0, mPos); - mWriter.flush(); - } - mPos = 0; - } - } - - public int getDepth() { - throw new UnsupportedOperationException(); - } - - public boolean getFeature(String name) { - throw new UnsupportedOperationException(); - } - - public String getName() { - throw new UnsupportedOperationException(); - } - - public String getNamespace() { - throw new UnsupportedOperationException(); - } - - public String getPrefix(String namespace, boolean generatePrefix) - throws IllegalArgumentException { - throw new UnsupportedOperationException(); - } - - public Object getProperty(String name) { - throw new UnsupportedOperationException(); - } - - public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void processingInstruction(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void setFeature(String name, boolean state) throws IllegalArgumentException, - IllegalStateException { - if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { - mIndent = true; - return; - } - throw new UnsupportedOperationException(); - } - - public void setOutput(OutputStream os, String encoding) throws IOException, - IllegalArgumentException, IllegalStateException { - if (os == null) - throw new IllegalArgumentException(); - if (true) { - try { - mCharset = Charset.forName(encoding).newEncoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - } catch (IllegalCharsetNameException e) { - throw (UnsupportedEncodingException) (new UnsupportedEncodingException( - encoding).initCause(e)); - } catch (UnsupportedCharsetException e) { - throw (UnsupportedEncodingException) (new UnsupportedEncodingException( - encoding).initCause(e)); - } - mOutputStream = os; - } else { - setOutput( - encoding == null - ? new OutputStreamWriter(os) - : new OutputStreamWriter(os, encoding)); - } - } - - public void setOutput(Writer writer) throws IOException, IllegalArgumentException, - IllegalStateException { - mWriter = writer; - } - - public void setPrefix(String prefix, String namespace) throws IOException, - IllegalArgumentException, IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void setProperty(String name, Object value) throws IllegalArgumentException, - IllegalStateException { - throw new UnsupportedOperationException(); - } - - public void startDocument(String encoding, Boolean standalone) throws IOException, - IllegalArgumentException, IllegalStateException { - append("<?xml version='1.0' encoding='utf-8'"); - if (standalone != null) { - append(" standalone='" + (standalone ? "yes" : "no") + "'"); - } - append(" ?>\n"); - mLineStart = true; - } - - public XmlSerializer startTag(String namespace, String name) throws IOException, - IllegalArgumentException, IllegalStateException { - if (mInTag) { - append(">\n"); - } - if (mIndent) { - appendIndent(mNesting); - } - mNesting++; - append('<'); - if (namespace != null) { - append(namespace); - append(':'); - } - append(name); - mInTag = true; - mLineStart = false; - return this; - } - - public XmlSerializer text(char[] buf, int start, int len) throws IOException, - IllegalArgumentException, IllegalStateException { - if (mInTag) { - append(">"); - mInTag = false; - } - escapeAndAppendString(buf, start, len); - if (mIndent) { - mLineStart = buf[start+len-1] == '\n'; - } - return this; - } - - public XmlSerializer text(String text) throws IOException, IllegalArgumentException, - IllegalStateException { - if (mInTag) { - append(">"); - mInTag = false; - } - escapeAndAppendString(text); - if (mIndent) { - mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); - } - return this; - } - -} diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java index 282fdad294eb..ba2ea88e8e01 100644 --- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java +++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java @@ -125,6 +125,9 @@ public class AutomaticZenRuleTest { Field configActivity = Class.forName(CLASS).getDeclaredField("configurationActivity"); configActivity.setAccessible(true); configActivity.set(rule, new ComponentName(longString, longString)); + Field trigger = Class.forName(CLASS).getDeclaredField("mTriggerDescription"); + trigger.setAccessible(true); + trigger.set(rule, longString); } catch (NoSuchFieldException e) { fail(e.toString()); } catch (ClassNotFoundException e) { @@ -149,5 +152,6 @@ public class AutomaticZenRuleTest { fromParcel.getOwner().getPackageName().length()); assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, fromParcel.getOwner().getClassName().length()); + assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length()); } } diff --git a/core/tests/coretests/src/android/app/time/TEST_MAPPING b/core/tests/coretests/src/android/app/time/TEST_MAPPING deleted file mode 100644 index 9d711a271642..000000000000 --- a/core/tests/coretests/src/android/app/time/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.time." - } - ] - } - ] -} diff --git a/core/tests/coretests/src/android/app/timedetector/OWNERS b/core/tests/coretests/src/android/app/timedetector/OWNERS deleted file mode 100644 index c6124734bc5c..000000000000 --- a/core/tests/coretests/src/android/app/timedetector/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 847766 -include /core/java/android/app/timedetector/OWNERS diff --git a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING deleted file mode 100644 index 6c4d48de30a3..000000000000 --- a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.timedetector." - } - ] - } - ] -} diff --git a/core/tests/coretests/src/android/app/timezonedetector/OWNERS b/core/tests/coretests/src/android/app/timezonedetector/OWNERS deleted file mode 100644 index 2e9c3246e4fc..000000000000 --- a/core/tests/coretests/src/android/app/timezonedetector/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 847766 -include /core/java/android/app/timezonedetector/OWNERS diff --git a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING deleted file mode 100644 index 8872f642f604..000000000000 --- a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.timezonedetector." - } - ] - } - ] -} diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING index 7ebda0062335..bec72d988e74 100644 --- a/core/tests/coretests/src/android/service/TEST_MAPPING +++ b/core/tests/coretests/src/android/service/TEST_MAPPING @@ -10,7 +10,6 @@ {"include-filter": "android.service.notification"}, {"include-filter": "android.service.quicksettings"}, {"include-filter": "android.service.settings.suggestions"}, - {"include-filter": "android.service.timezone"}, {"exclude-annotation": "org.junit.Ignore"} ] } diff --git a/core/tests/coretests/src/android/service/timezone/OWNERS b/core/tests/coretests/src/android/service/timezone/OWNERS deleted file mode 100644 index 811638805996..000000000000 --- a/core/tests/coretests/src/android/service/timezone/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 847766 -include /core/java/android/service/timezone/OWNERS diff --git a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING b/core/tests/coretests/src/android/service/timezone/TEST_MAPPING deleted file mode 100644 index 46f476f43861..000000000000 --- a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.service.timezone." - } - ] - } - ] -} diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index e7117a7da0e9..dfe6cf81813d 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -49,6 +49,7 @@ import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.app.UiModeManager; import android.content.Context; +import android.graphics.ForceDarkType; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; import android.os.SystemProperties; @@ -593,7 +594,7 @@ public class ViewRootImplTest { mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) ); - assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse(); + assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); } @Test @@ -613,7 +614,8 @@ public class ViewRootImplTest { mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) ); - assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue(); + assertThat(mViewRootImpl.determineForceDarkType()) + .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK); } @Test @@ -634,7 +636,7 @@ public class ViewRootImplTest { mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) ); - assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse(); + assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE); } @Test @@ -654,7 +656,7 @@ public class ViewRootImplTest { mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()) ); - assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue(); + assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK); } private boolean setForceDarkSysProp(boolean isForceDarkEnabled) { diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp new file mode 100644 index 000000000000..51181a8a7b13 --- /dev/null +++ b/core/tests/timetests/Android.bp @@ -0,0 +1,25 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "FrameworksTimeCoreTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "device-time-shell-utils", + "junit", + "junit-params", + "mockito-target-minus-junit4", + "platform-test-annotations", + "truth", + ], + libs: ["android.test.runner"], + certificate: "platform", + test_suites: ["device-tests"], +} diff --git a/core/tests/timetests/AndroidManifest.xml b/core/tests/timetests/AndroidManifest.xml new file mode 100644 index 000000000000..330e05fb407d --- /dev/null +++ b/core/tests/timetests/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.time"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.coretests.time" + android:label="Frameworks Time Core Tests" /> + +</manifest> diff --git a/core/tests/timetests/AndroidTest.xml b/core/tests/timetests/AndroidTest.xml new file mode 100644 index 000000000000..d2d1255768d8 --- /dev/null +++ b/core/tests/timetests/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Time Core Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksTimeCoreTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.coretests.time" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/core/tests/coretests/src/android/app/time/OWNERS b/core/tests/timetests/OWNERS index 292cb72f2dbe..292cb72f2dbe 100644 --- a/core/tests/coretests/src/android/app/time/OWNERS +++ b/core/tests/timetests/OWNERS diff --git a/core/tests/timetests/TEST_MAPPING b/core/tests/timetests/TEST_MAPPING new file mode 100644 index 000000000000..57480445a7b0 --- /dev/null +++ b/core/tests/timetests/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ + { + "name": "FrameworksTimeCoreTests" + } + ] +} diff --git a/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java b/core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java index 2e1e09c14d5e..2e1e09c14d5e 100644 --- a/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java +++ b/core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java index ae3357a75fd1..ae3357a75fd1 100644 --- a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java +++ b/core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java diff --git a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java index 384a5a82181b..384a5a82181b 100644 --- a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java +++ b/core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java diff --git a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java index 13e5e14ce01c..d2c9c3a35ce5 100644 --- a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java +++ b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java @@ -47,6 +47,10 @@ public final class ParcelableTestSupport { return toReturn; } + /** + * Asserts that the parameter can be parceled and unparceled and return an object considered + * equal to the original. + */ public static <T extends Parcelable> void assertRoundTripParcelable(T instance) { assertEqualsAndHashCode(instance, roundTripParcelable(instance)); } diff --git a/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java index e6e2371d312f..e6e2371d312f 100644 --- a/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java +++ b/core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java index 3ead97f1528a..3ead97f1528a 100644 --- a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java +++ b/core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeManagerTest.java b/core/tests/timetests/src/android/app/time/TimeManagerTest.java index 89c8d6a2e42b..89c8d6a2e42b 100644 --- a/core/tests/coretests/src/android/app/time/TimeManagerTest.java +++ b/core/tests/timetests/src/android/app/time/TimeManagerTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeStateTest.java b/core/tests/timetests/src/android/app/time/TimeStateTest.java index 50da3a68a140..50da3a68a140 100644 --- a/core/tests/coretests/src/android/app/time/TimeStateTest.java +++ b/core/tests/timetests/src/android/app/time/TimeStateTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java index e368d2815855..e368d2815855 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java +++ b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java index 4ad3e41383aa..4ad3e41383aa 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java +++ b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java b/core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java index 4ef60a3306aa..4ef60a3306aa 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java +++ b/core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java diff --git a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java b/core/tests/timetests/src/android/app/time/TimeZoneStateTest.java index 0b816ecc0489..0b816ecc0489 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java +++ b/core/tests/timetests/src/android/app/time/TimeZoneStateTest.java diff --git a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java b/core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java index 0b485c0af6ed..0b485c0af6ed 100644 --- a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java +++ b/core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java index b06845602a87..b06845602a87 100644 --- a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java +++ b/core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java diff --git a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java index 2716ebf53019..2716ebf53019 100644 --- a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java +++ b/core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java diff --git a/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java index 8e1f0a19b4ab..8e1f0a19b4ab 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java +++ b/core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java diff --git a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java index 4efaed11168e..59a73048bc07 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java +++ b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java @@ -29,10 +29,17 @@ import java.util.List; public final class ShellCommandTestSupport { private ShellCommandTestSupport() {} + /** + * Returns a {@link ShellCommand} from the supplied String, where elements of the command are + * separated with spaces. No escaping is performed. + */ public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) { return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" "))); } + /** + * Returns a {@link ShellCommand} from the supplied list of command line elements. + */ public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) { ShellCommand command = mock(ShellCommand.class); class ArgProvider { diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java index 42cb856694bc..42cb856694bc 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java +++ b/core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java index 26e424513092..26e424513092 100644 --- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java +++ b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java index 9b2455982f70..cf37db680326 100644 --- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java +++ b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java @@ -32,6 +32,9 @@ import android.service.timezone.TimeZoneProviderStatus.OperationStatus; import androidx.test.filters.SmallTest; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + import org.junit.Test; import org.junit.runner.RunWith; @@ -39,9 +42,6 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - /** Non-SDK tests. See CTS for SDK API tests. */ @RunWith(JUnitParamsRunner.class) @SmallTest @@ -83,6 +83,7 @@ public class TimeZoneProviderStatusTest { providerStatus.couldEnableTelephonyFallback()); } + /** Parameters for {@link #couldEnableTelephonyFallback}. */ public static Integer[][] couldEnableTelephonyFallbackParams() { List<Integer[]> params = new ArrayList<>(); @DependencyStatus int[] dependencyStatuses = diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java index a091036f8eaf..a091036f8eaf 100644 --- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java +++ b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java diff --git a/graphics/java/android/graphics/ForceDarkType.java b/graphics/java/android/graphics/ForceDarkType.java new file mode 100644 index 000000000000..396b03703bb9 --- /dev/null +++ b/graphics/java/android/graphics/ForceDarkType.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The style of force dark to use in {@link HardwareRenderer}. + * + * You must keep this in sync with the C++ enum ForceDarkType in + * frameworks/base/libs/hwui/utils/ForceDark.h + * + * @hide + */ +public class ForceDarkType { + /** + * Force dark disabled: normal, default operation. + * + * @hide + */ + public static final int NONE = 0; + + /** + * Use force dark + * @hide + */ + public static final int FORCE_DARK = 1; + + /** + * Force force-dark. {@see Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED} + * @hide */ + public static final int FORCE_INVERT_COLOR_DARK = 2; + + /** @hide */ + @IntDef({ + NONE, + FORCE_DARK, + FORCE_INVERT_COLOR_DARK, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ForceDarkTypeDef {} + +} diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 8cd262e783d8..20e393eaee6d 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -182,7 +182,7 @@ public class HardwareRenderer { /** @hide */ protected RenderNode mRootNode; private boolean mOpaque = true; - private boolean mForceDark = false; + private int mForceDark = ForceDarkType.NONE; private @ActivityInfo.ColorMode int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT; private float mDesiredSdrHdrRatio = 1f; @@ -571,10 +571,10 @@ public class HardwareRenderer { * Whether or not the force-dark feature should be used for this renderer. * @hide */ - public boolean setForceDark(boolean enable) { - if (mForceDark != enable) { - mForceDark = enable; - nSetForceDark(mNativeProxy, enable); + public boolean setForceDark(@ForceDarkType.ForceDarkTypeDef int type) { + if (mForceDark != type) { + mForceDark = type; + nSetForceDark(mNativeProxy, type); return true; } return false; @@ -1597,7 +1597,7 @@ public class HardwareRenderer { private static native void nAllocateBuffers(long nativeProxy); - private static native void nSetForceDark(long nativeProxy, boolean enabled); + private static native void nSetForceDark(long nativeProxy, int type); private static native void nSetDisplayDensityDpi(int densityDpi); diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 0e59e9ad744d..29bdd5ce0c9e 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -28,3 +28,10 @@ flag { description: "Enables invoking split contextually" bug: "276361926" } + +flag { + name: "enable_taskbar_navbar_unification" + namespace: "multitasking" + description: "Enables taskbar / navbar unification" + bug: "309671494" +} diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index b556150e2ab9..3e66bbbf8d39 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -72,7 +72,7 @@ <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead [CHAR LIMIT=NONE] --> <string name="dock_non_resizeble_failed_to_dock_text">App does not support split screen</string> <!-- Warning message when we try to dock an app not supporting multiple instances split into multiple sides [CHAR LIMIT=NONE] --> - <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window.</string> + <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window</string> <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] --> <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string> <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. --> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java index 5cf9175073c0..8241e1a481ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java @@ -136,6 +136,7 @@ class ActivityEmbeddingAnimationAdapter { /** Called on frame update. */ final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) { + mTransformation.clear(); // Extract the transformation to the current time. mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()), mTransformation); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d28bb499c907..3e131bc44d39 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -40,6 +40,7 @@ #ifdef __ANDROID__ #include "include/gpu/ganesh/SkImageGanesh.h" #endif +#include "utils/ForceDark.h" #include "utils/MathUtils.h" #include "utils/StringUtils.h" @@ -403,16 +404,21 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { deleteDisplayList(observer, info); mDisplayList = std::move(mStagingDisplayList); if (mDisplayList) { - WebViewSyncData syncData { - .applyForceDark = info && !info->disableForceDark - }; + WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)}; mDisplayList.syncContents(syncData); handleForceDark(info); } } +inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) { + return CC_UNLIKELY( + info && + (!info->disableForceDark || + info->forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK)); +} + void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { - if (CC_LIKELY(!info || info->disableForceDark)) { + if (!shouldEnableForceDark(info)) { return; } auto usage = usageHint(); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c959db37474b..1f3834be5bef 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -233,6 +233,7 @@ private: void syncProperties(); void syncDisplayList(TreeObserver& observer, TreeInfo* info); void handleForceDark(TreeInfo* info); + bool shouldEnableForceDark(TreeInfo* info); void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer); void pushStagingPropertiesChanges(TreeInfo& info); diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp index 750f869e2551..717157c09a53 100644 --- a/libs/hwui/TreeInfo.cpp +++ b/libs/hwui/TreeInfo.cpp @@ -24,7 +24,8 @@ TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContex : mode(mode) , prepareTextures(mode == MODE_FULL) , canvasContext(canvasContext) - , disableForceDark(canvasContext.useForceDark() ? 0 : 1) + , disableForceDark(canvasContext.getForceDarkType() == ForceDarkType::NONE ? 1 : 0) + , forceDarkType(canvasContext.getForceDarkType()) , screenSize(canvasContext.getNextFrameSize()) {} } // namespace android::uirenderer diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index ea25f68d7170..88449f3a3572 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -24,6 +24,7 @@ #include "Properties.h" #include "SkSize.h" #include "SkippedFrameInfo.h" +#include "utils/ForceDark.h" #include "utils/Macros.h" namespace android { @@ -97,6 +98,7 @@ public: bool updateWindowPositions = false; int disableForceDark; + ForceDarkType forceDarkType = ForceDarkType::NONE; const SkISize screenSize; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 422ffeaecfd0..d15b1680de94 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -35,6 +35,7 @@ #include <gui/TraceUtils.h> #include <include/encode/SkPngEncoder.h> #include <inttypes.h> +#include <log/log.h> #include <media/NdkImage.h> #include <media/NdkImageReader.h> #include <nativehelper/JNIPlatformHelp.h> @@ -53,11 +54,11 @@ #include <algorithm> #include <atomic> -#include <log/log.h> #include <vector> #include "JvmErrorReporter.h" #include "android_graphics_HardwareRendererObserver.h" +#include "utils/ForceDark.h" namespace android { @@ -824,10 +825,10 @@ static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject c proxy->allocateBuffers(); } -static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, - jlong proxyPtr, jboolean enable) { +static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, jlong proxyPtr, + jint type) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - proxy->setForceDark(enable); + proxy->setForceDark(static_cast<ForceDarkType>(type)); } static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { @@ -1016,7 +1017,7 @@ static const JNINativeMethod gMethods[] = { {"nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess}, {"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority}, {"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers}, - {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark}, + {"nSetForceDark", "(JI)V", (void*)android_view_ThreadedRenderer_setForceDark}, {"nSetDisplayDensityDpi", "(I)V", (void*)android_view_ThreadedRenderer_setDisplayDensityDpi}, {"nInitDisplayInfo", "(IIFIJJZZ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo}, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 7fac0c9776ac..9e3bb79f03f3 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -969,6 +969,7 @@ void CanvasContext::buildLayer(RenderNode* node) { // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); + ScopedActiveContext activeContext(this); TreeInfo info(TreeInfo::MODE_FULL, *this); info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 37e4f7ecca54..be9b649030ae 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -46,6 +46,7 @@ #include "renderstate/RenderState.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" +#include "utils/ForceDark.h" #include "utils/RingBuffer.h" namespace android { @@ -194,11 +195,9 @@ public: mRenderPipeline->setPictureCapturedCallback(callback); } - void setForceDark(bool enable) { mUseForceDark = enable; } + void setForceDark(ForceDarkType type) { mForceDarkType = type; } - bool useForceDark() { - return mUseForceDark; - } + ForceDarkType getForceDarkType() { return mForceDarkType; } SkISize getNextFrameSize() const; @@ -321,7 +320,7 @@ private: nsecs_t mLastDropVsync = 0; bool mOpaque; - bool mUseForceDark = false; + ForceDarkType mForceDarkType = ForceDarkType::NONE; LightInfo mLightInfo; LightGeometry mLightGeometry = {{0, 0, 0}, 0}; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index be163bad77a7..c3c136feef69 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -417,8 +417,8 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) }); } -void RenderProxy::setForceDark(bool enable) { - mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); }); +void RenderProxy::setForceDark(ForceDarkType type) { + mRenderThread.queue().post([this, type]() { mContext->setForceDark(type); }); } void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 47c1b0cd28e5..f2d8e94c7bd2 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -31,6 +31,7 @@ #include "DrawFrameTask.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" +#include "utils/ForceDark.h" class SkBitmap; class SkPicture; @@ -142,7 +143,7 @@ public: void addFrameMetricsObserver(FrameMetricsObserver* observer); void removeFrameMetricsObserver(FrameMetricsObserver* observer); - void setForceDark(bool enable); + void setForceDark(ForceDarkType type); static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request); static void prepareToDraw(Bitmap& bitmap); diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java b/libs/hwui/utils/ForceDark.h index 03868f99663d..28538c4b7a7b 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java +++ b/libs/hwui/utils/ForceDark.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,21 @@ * limitations under the License. */ -package com.android.settingslib.widget; +#ifndef FORCEDARKUTILS_H +#define FORCEDARKUTILS_H -import android.widget.Switch; - -import com.android.settingslib.widget.mainswitch.R; +namespace android { +namespace uirenderer { /** - * Called when the checked state of the Switch has changed. + * The type of force dark set on the renderer, if any. + * + * This should stay in sync with the java @IntDef in + * frameworks/base/graphics/java/android/graphics/ForceDarkType.java */ -public interface OnMainSwitchChangeListener { - /** - * @param switchView The Switch view whose state has changed. - * @param isChecked The new checked state of switchView. - */ - void onSwitchChanged(Switch switchView, boolean isChecked); -} +enum class ForceDarkType : __uint8_t { NONE = 0, FORCE_DARK = 1, FORCE_INVERT_COLOR_DARK = 2 }; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // FORCEDARKUTILS_H
\ No newline at end of file diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp index c883b1f26160..2a89a9940936 100644 --- a/packages/CredentialManager/wear/Android.bp +++ b/packages/CredentialManager/wear/Android.bp @@ -35,6 +35,7 @@ android_app { "androidx.compose.ui_ui", "androidx.compose.ui_ui-tooling", "androidx.core_core-ktx", + "androidx.hilt_hilt-navigation-compose", "androidx.lifecycle_lifecycle-extensions", "androidx.lifecycle_lifecycle-livedata", "androidx.lifecycle_lifecycle-runtime-ktx", diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 0a63cb74a25a..f2df64aee22e 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -21,16 +21,10 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.wear.compose.material.MaterialTheme import com.android.credentialmanager.ui.WearApp -import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen import com.google.android.horologist.annotations.ExperimentalHorologistApi -import com.google.android.horologist.compose.layout.belowTimeTextPreview import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint(ComponentActivity::class) class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { @@ -42,50 +36,14 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { super.onCreate(savedInstanceState) setTheme(android.R.style.Theme_DeviceDefault) - - // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a - // workaround. Once the issue is fixed, remove the "else" bracket and leave only the - // contents of the "if" bracket. - if (false) { - setContent { - MaterialTheme { - WearApp( - viewModel = viewModel, - onCloseApp = ::finish, - ) - } - } - } else { - // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.uiState.collect { uiState -> - when (uiState) { - CredentialSelectorUiState.Idle -> { - // Don't display anything, assuming that there should be minimal latency - // to parse the Credential Manager intent and define the state of the - // app. If latency is big, then a "loading" screen should be displayed - // to the user. - } - - is CredentialSelectorUiState.Get -> { - setContent { - MaterialTheme { - SinglePasswordScreen( - columnState = belowTimeTextPreview(), - onCloseApp = ::finish, - ) - } - } - } - - else -> finish() - } - } - } + setContent { + MaterialTheme { + WearApp( + viewModel = viewModel, + onCloseApp = ::finish, + ) } } - viewModel.onNewIntent(intent) } diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt index 81a067289d89..c28df3e8895a 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt @@ -27,8 +27,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import com.android.credentialmanager.R import com.android.credentialmanager.TAG import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract @@ -47,7 +47,7 @@ fun SinglePasswordScreen( columnState: ScalingLazyColumnState, onCloseApp: () -> Unit, modifier: Modifier = Modifier, - viewModel: SinglePasswordScreenViewModel = viewModel(), + viewModel: SinglePasswordScreenViewModel = hiltViewModel(), ) { viewModel.initialize() diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 600115545a16..2b5fcd807899 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -24,8 +24,8 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.LinearLayout; -import android.widget.Switch; import android.widget.TextView; import androidx.annotation.ColorInt; @@ -41,9 +41,9 @@ import java.util.List; * This component is used as the main switch of the page * to enable or disable the prefereces on the page. */ -public class MainSwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener { +public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListener { - private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>(); + private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>(); @ColorInt private int mBackgroundColor; @@ -51,8 +51,8 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec private int mBackgroundActivatedColor; protected TextView mTextView; - protected Switch mSwitch; - private View mFrameView; + protected CompoundButton mSwitch; + private final View mFrameView; public MainSwitchBar(Context context) { this(context, null); @@ -84,8 +84,8 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec setClickable(true); mFrameView = findViewById(R.id.frame); - mTextView = (TextView) findViewById(R.id.switch_text); - mSwitch = (Switch) findViewById(android.R.id.switch_widget); + mTextView = findViewById(R.id.switch_text); + mSwitch = findViewById(android.R.id.switch_widget); addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked)); if (mSwitch.getVisibility() == VISIBLE) { @@ -136,13 +136,6 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec } /** - * Return the Switch - */ - public final Switch getSwitch() { - return mSwitch; - } - - /** * Set the title text */ public void setTitle(CharSequence text) { @@ -192,7 +185,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec /** * Adds a listener for switch changes */ - public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) { + public void addOnSwitchChangeListener(OnCheckedChangeListener listener) { if (!mSwitchChangeListeners.contains(listener)) { mSwitchChangeListeners.add(listener); } @@ -201,7 +194,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec /** * Remove a listener for switch changes */ - public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { + public void removeOnSwitchChangeListener(OnCheckedChangeListener listener) { mSwitchChangeListeners.remove(listener); } @@ -223,9 +216,8 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec private void propagateChecked(boolean isChecked) { setBackground(isChecked); - final int count = mSwitchChangeListeners.size(); - for (int n = 0; n < count; n++) { - mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked); + for (OnCheckedChangeListener changeListener : mSwitchChangeListeners) { + changeListener.onCheckedChanged(mSwitch, isChecked); } } diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 11a680466ecf..b294d4e3ad19 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -19,24 +19,25 @@ package com.android.settingslib.widget; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.widget.Switch; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.preference.PreferenceViewHolder; import androidx.preference.TwoStatePreference; +import com.android.settingslib.widget.mainswitch.R; + import java.util.ArrayList; import java.util.List; -import com.android.settingslib.widget.mainswitch.R; - /** * MainSwitchPreference is a Preference with a customized Switch. * This component is used as the main switch of the page * to enable or disable the prefereces on the page. */ -public class MainSwitchPreference extends TwoStatePreference implements OnMainSwitchChangeListener { +public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener { - private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>(); + private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>(); private MainSwitchBar mMainSwitchBar; @@ -120,7 +121,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw } @Override - public void onSwitchChanged(Switch switchView, boolean isChecked) { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { super.setChecked(isChecked); } @@ -138,7 +139,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw /** * Adds a listener for switch changes */ - public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) { + public void addOnSwitchChangeListener(OnCheckedChangeListener listener) { if (!mSwitchChangeListeners.contains(listener)) { mSwitchChangeListeners.add(listener); } @@ -151,7 +152,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw /** * Remove a listener for switch changes */ - public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { + public void removeOnSwitchChangeListener(OnCheckedChangeListener listener) { mSwitchChangeListeners.remove(listener); if (mMainSwitchBar != null) { mMainSwitchBar.removeOnSwitchChangeListener(listener); @@ -159,7 +160,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw } private void registerListenerToSwitchBar() { - for (OnMainSwitchChangeListener listener : mSwitchChangeListeners) { + for (OnCheckedChangeListener listener : mSwitchChangeListeners) { mMainSwitchBar.addOnSwitchChangeListener(listener); } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.kt new file mode 100644 index 000000000000..2c60db4e76c7 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.framework.common + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.UserHandle +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn + +/** + * A [BroadcastReceiver] flow for the given [intentFilter]. + */ +fun Context.broadcastReceiverAsUserFlow( + intentFilter: IntentFilter, + userHandle: UserHandle, +): Flow<Intent> = callbackFlow { + val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + trySend(intent) + } + } + registerReceiverAsUser( + broadcastReceiver, + userHandle, + intentFilter, + null, + null, + Context.RECEIVER_NOT_EXPORTED, + ) + + awaitClose { unregisterReceiver(broadcastReceiver) } +}.conflate().flowOn(Dispatchers.Default) diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt index ad907cfeb5bf..7d6ee1928111 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt @@ -17,14 +17,14 @@ package com.android.settingslib.spaprivileged.framework.compose import android.content.BroadcastReceiver -import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.UserHandle import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import com.android.settingslib.spa.framework.compose.LifecycleEffect +import androidx.compose.ui.platform.LocalLifecycleOwner +import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle +import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverAsUserFlow /** * A [BroadcastReceiver] which registered when on start and unregistered when on stop. @@ -35,27 +35,6 @@ fun DisposableBroadcastReceiverAsUser( userHandle: UserHandle, onReceive: (Intent) -> Unit, ) { - val context = LocalContext.current - val broadcastReceiver = remember { - object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - onReceive(intent) - } - } - } - LifecycleEffect( - onStart = { - context.registerReceiverAsUser( - broadcastReceiver, - userHandle, - intentFilter, - null, - null, - Context.RECEIVER_NOT_EXPORTED, - ) - }, - onStop = { - context.unregisterReceiver(broadcastReceiver) - }, - ) + LocalContext.current.broadcastReceiverAsUserFlow(intentFilter, userHandle) + .collectLatestWithLifecycle(LocalLifecycleOwner.current, action = onReceive) } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt new file mode 100644 index 000000000000..dfb8e22c7b52 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.framework.common + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.eq +import org.mockito.kotlin.isNull +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +class BroadcastReceiverAsUserFlowTest { + + private var registeredBroadcastReceiver: BroadcastReceiver? = null + + private val context = mock<Context> { + on { + registerReceiverAsUser( + any(), + eq(USER_HANDLE), + eq(INTENT_FILTER), + isNull(), + isNull(), + eq(Context.RECEIVER_NOT_EXPORTED), + ) + } doAnswer { + registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver + null + } + } + + @Test + fun broadcastReceiverAsUserFlow_registered() = runBlocking { + val flow = context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE) + + flow.firstWithTimeoutOrNull() + + assertThat(registeredBroadcastReceiver).isNotNull() + } + + @Test + fun broadcastReceiverAsUserFlow_isCalledOnReceive() = runBlocking { + var onReceiveIsCalled = false + launch { + context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE).first { + onReceiveIsCalled = true + true + } + } + + delay(100) + registeredBroadcastReceiver!!.onReceive(context, Intent()) + delay(100) + + assertThat(onReceiveIsCalled).isTrue() + } + + private companion object { + val USER_HANDLE: UserHandle = UserHandle.of(0) + + val INTENT_FILTER = IntentFilter() + } +} diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt index 2c8fb66fab0a..f812f959db32 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt @@ -23,38 +23,32 @@ import android.content.IntentFilter import android.os.UserHandle import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.test.junit4.createComposeRule +import androidx.lifecycle.testing.TestLifecycleOwner import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat -import org.junit.Before +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer import org.mockito.kotlin.eq import org.mockito.kotlin.isNull -import org.mockito.kotlin.whenever +import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class DisposableBroadcastReceiverAsUserTest { @get:Rule val composeTestRule = createComposeRule() - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() - - @Mock - private lateinit var context: Context - private var registeredBroadcastReceiver: BroadcastReceiver? = null - @Before - fun setUp() { - whenever( - context.registerReceiverAsUser( + private val context = mock<Context> { + on { + registerReceiverAsUser( any(), eq(USER_HANDLE), eq(INTENT_FILTER), @@ -62,7 +56,7 @@ class DisposableBroadcastReceiverAsUserTest { isNull(), eq(Context.RECEIVER_NOT_EXPORTED), ) - ).then { + } doAnswer { registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver null } @@ -71,7 +65,10 @@ class DisposableBroadcastReceiverAsUserTest { @Test fun broadcastReceiver_registered() { composeTestRule.setContent { - CompositionLocalProvider(LocalContext provides context) { + CompositionLocalProvider( + LocalContext provides context, + LocalLifecycleOwner provides TestLifecycleOwner(), + ) { DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) {} } } @@ -80,10 +77,13 @@ class DisposableBroadcastReceiverAsUserTest { } @Test - fun broadcastReceiver_isCalledOnReceive() { + fun broadcastReceiver_isCalledOnReceive() = runBlocking { var onReceiveIsCalled = false composeTestRule.setContent { - CompositionLocalProvider(LocalContext provides context) { + CompositionLocalProvider( + LocalContext provides context, + LocalLifecycleOwner provides TestLifecycleOwner(), + ) { DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) { onReceiveIsCalled = true } @@ -91,6 +91,7 @@ class DisposableBroadcastReceiverAsUserTest { } registeredBroadcastReceiver!!.onReceive(context, Intent()) + delay(100) assertThat(onReceiveIsCalled).isTrue() } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt index 840bca8b14ec..44973a743c76 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt @@ -19,6 +19,8 @@ package com.android.settingslib.spaprivileged.model.app import android.content.Context import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo +import android.content.pm.FakeFeatureFlagsImpl +import android.content.pm.Flags import android.content.pm.PackageManager import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.PackageManager.ResolveInfoFlags @@ -29,76 +31,62 @@ import android.os.UserManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.internal.R -import com.android.settingslib.spaprivileged.framework.common.userManager import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import android.content.pm.FakeFeatureFlagsImpl -import android.content.pm.Flags @RunWith(AndroidJUnit4::class) class AppListRepositoryTest { - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() - - @Spy - private val context: Context = ApplicationProvider.getApplicationContext() - - @Mock - private lateinit var resources: Resources - - @Mock - private lateinit var packageManager: PackageManager - - @Mock - private lateinit var userManager: UserManager - - private lateinit var repository: AppListRepository + private val resources = mock<Resources> { + on { getStringArray(R.array.config_hideWhenDisabled_packageNames) } doReturn emptyArray() + } - @Before - fun setUp() { - whenever(context.resources).thenReturn(resources) - whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)) - .thenReturn(emptyArray()) - whenever(context.packageManager).thenReturn(packageManager) - whenever(context.userManager).thenReturn(userManager) - whenever(packageManager.getInstalledModules(any())).thenReturn(emptyList()) - whenever(packageManager.getHomeActivities(any())).thenAnswer { + private val packageManager = mock<PackageManager> { + on { getInstalledModules(any()) } doReturn emptyList() + on { getHomeActivities(any()) } doAnswer { @Suppress("UNCHECKED_CAST") val resolveInfos = it.arguments[0] as MutableList<ResolveInfo> resolveInfos += resolveInfoOf(packageName = HOME_APP.packageName) null } - whenever( - packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), any<Int>()) - ).thenReturn(listOf(resolveInfoOf(packageName = IN_LAUNCHER_APP.packageName))) - whenever(userManager.getUserInfo(ADMIN_USER_ID)).thenReturn(UserInfo().apply { + on { queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), any<Int>()) } doReturn + listOf(resolveInfoOf(packageName = IN_LAUNCHER_APP.packageName)) + } + + private val mockUserManager = mock<UserManager> { + on { getUserInfo(ADMIN_USER_ID) } doReturn UserInfo().apply { flags = UserInfo.FLAG_ADMIN - }) - whenever(userManager.getProfileIdsWithDisabled(ADMIN_USER_ID)) - .thenReturn(intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID)) + } + on { getProfileIdsWithDisabled(ADMIN_USER_ID) } doReturn + intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID) + } - repository = AppListRepositoryImpl(context) + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { resources } doReturn resources + on { packageManager } doReturn packageManager + on { getSystemService(UserManager::class.java) } doReturn mockUserManager } + private val repository = AppListRepositoryImpl(context) + private fun mockInstalledApplications(apps: List<ApplicationInfo>, userId: Int) { - whenever( - packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId)) - ).thenReturn(apps) + packageManager.stub { + on { getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId)) } doReturn + apps + } } @Test @@ -135,13 +123,13 @@ class AppListRepositoryTest { ) assertThat(appList).containsExactly(NORMAL_APP) - argumentCaptor<ApplicationInfoFlags> { + val flags = argumentCaptor<ApplicationInfoFlags> { verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID)) - assertThat(firstValue.value).isEqualTo( - PackageManager.MATCH_DISABLED_COMPONENTS or - PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS - ) - } + }.firstValue + assertThat(flags.value).isEqualTo( + PackageManager.MATCH_DISABLED_COMPONENTS or + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS + ) } @Test @@ -154,11 +142,10 @@ class AppListRepositoryTest { ) assertThat(appList).containsExactly(NORMAL_APP) - argumentCaptor<ApplicationInfoFlags> { + val flags = argumentCaptor<ApplicationInfoFlags> { verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID)) - assertThat(firstValue.value and PackageManager.MATCH_ANY_USER.toLong()) - .isGreaterThan(0L) - } + }.firstValue + assertThat(flags.value and PackageManager.MATCH_ANY_USER.toLong()).isGreaterThan(0L) } @Test @@ -278,14 +265,14 @@ class AppListRepositoryTest { val appList = repository.loadApps(userId = ADMIN_USER_ID) assertThat(appList).containsExactly(NORMAL_APP, ARCHIVED_APP) - argumentCaptor<ApplicationInfoFlags> { + val flags = argumentCaptor<ApplicationInfoFlags> { verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID)) - assertThat(firstValue.value).isEqualTo( - (PackageManager.MATCH_DISABLED_COMPONENTS or - PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() or - PackageManager.MATCH_ARCHIVED_PACKAGES - ) - } + }.firstValue + assertThat(flags.value).isEqualTo( + (PackageManager.MATCH_DISABLED_COMPONENTS or + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() or + PackageManager.MATCH_ARCHIVED_PACKAGES + ) } @Test @@ -294,13 +281,13 @@ class AppListRepositoryTest { val appList = repository.loadApps(userId = ADMIN_USER_ID) assertThat(appList).containsExactly(NORMAL_APP) - argumentCaptor<ApplicationInfoFlags> { + val flags = argumentCaptor<ApplicationInfoFlags> { verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID)) - assertThat(firstValue.value).isEqualTo( - PackageManager.MATCH_DISABLED_COMPONENTS or - PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS - ) - } + }.firstValue + assertThat(flags.value).isEqualTo( + PackageManager.MATCH_DISABLED_COMPONENTS or + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS + ) } @Test diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 0e7b79ba297a..119aef6da12e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -136,7 +136,7 @@ public class LocalBluetoothProfileManager { /** * create profile instance according to bluetooth supported profile list */ - void updateLocalProfiles() { + synchronized void updateLocalProfiles() { List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles(); if (CollectionUtils.isEmpty(supportedList)) { if (DEBUG) Log.d(TAG, "supportedList is null"); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java index 942e91525f02..74a282fbd106 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java @@ -21,30 +21,25 @@ import static android.graphics.text.LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import android.text.TextUtils; import android.view.View; -import android.widget.Switch; +import android.widget.CompoundButton; import android.widget.TextView; +import androidx.test.core.app.ApplicationProvider; + import com.android.settingslib.widget.mainswitch.R; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class MainSwitchBarTest { - private Context mContext; - private MainSwitchBar mBar; + private final Context mContext = ApplicationProvider.getApplicationContext(); + private final MainSwitchBar mBar = new MainSwitchBar(mContext); - @Before - public void setUp() { - mContext = RuntimeEnvironment.application; - mBar = new MainSwitchBar(mContext); - } + private final CompoundButton mSwitch = mBar.findViewById(android.R.id.switch_widget); @Test public void setChecked_true_shouldChecked() { @@ -60,7 +55,7 @@ public class MainSwitchBarTest { mBar.setTitle(title); final TextView textView = ((TextView) mBar.findViewById(R.id.switch_text)); - assertThat(textView.getText()).isEqualTo(title); + assertThat(textView.getText().toString()).isEqualTo(title); } @Test @@ -69,23 +64,18 @@ public class MainSwitchBarTest { mBar.setTitle(title); - final Switch switchObj = mBar.getSwitch(); - assertThat(TextUtils.isEmpty(switchObj.getContentDescription())).isTrue(); + assertThat(mSwitch.getContentDescription()).isNull(); } @Test public void getSwitch_shouldNotNull() { - final Switch switchObj = mBar.getSwitch(); - - assertThat(switchObj).isNotNull(); + assertThat(mSwitch).isNotNull(); } @Test public void getSwitch_shouldNotFocusableAndClickable() { - final Switch switchObj = mBar.getSwitch(); - - assertThat(switchObj.isFocusable()).isFalse(); - assertThat(switchObj.isClickable()).isFalse(); + assertThat(mSwitch.isFocusable()).isFalse(); + assertThat(mSwitch.isClickable()).isFalse(); } @Test diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index 6bc27160c0a8..c2fc0d9a4d91 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -29,6 +29,7 @@ import android.app.ActivityManager; import android.content.AttributionSource; import android.content.IContentProvider; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -219,14 +220,29 @@ public final class DeviceConfigService extends Binder { return lines; } + private static void log(String msg) { + if (Build.IS_DEBUGGABLE) { + Slog.wtf(TAG, msg); + } else { + Slog.e(TAG, msg); + } + } + public static List<String> listAllAconfigFlags(IContentProvider provider) { HashMap<String, String> allFlags = getAllFlags(provider); HashSet<String> aconfigFlagNames = getAconfigFlagNamesInDeviceConfig(); final ArrayList<String> lines = new ArrayList<>(); - for (String key : aconfigFlagNames) { - String val = allFlags.get(key); + for (String aconfigFlag : aconfigFlagNames) { + String val = allFlags.get(aconfigFlag); if (val != null) { - lines.add(key + "=" + val); + int idx = aconfigFlag.indexOf("/"); + if (idx == -1 || idx == aconfigFlag.length() - 1 || idx == 0) { + log("invalid flag entry in device config: " + aconfigFlag); + continue; + } + String aconfigFlagNameByPackage = aconfigFlag.substring(idx+1); + String namespace = aconfigFlag.substring(0, idx); + lines.add(aconfigFlagNameByPackage + " " + namespace + "=" + val); } } Collections.sort(lines); diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 56970d7e3005..d668c691ee0d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -154,7 +154,7 @@ private fun SceneScope.BouncerScene( Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, - isUserInputAreaVisible = true, + userInputAreaVisibility = UserInputAreaVisibility.FULL, modifier = childModifier, ) Layout.SIDE_BY_SIDE -> @@ -189,7 +189,7 @@ private fun SceneScope.BouncerScene( private fun Bouncer( viewModel: BouncerViewModel, dialogFactory: BouncerSceneDialogFactory, - isUserInputAreaVisible: Boolean, + userInputAreaVisibility: UserInputAreaVisibility, modifier: Modifier = Modifier, ) { val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState() @@ -214,12 +214,11 @@ private fun Bouncer( } Box(Modifier.weight(1f)) { - if (isUserInputAreaVisible) { - UserInputArea( - viewModel = viewModel, - modifier = Modifier.align(Alignment.Center), - ) - } + UserInputArea( + viewModel = viewModel, + visibility = userInputAreaVisibility, + modifier = Modifier.align(Alignment.Center), + ) } if (viewModel.isEmergencyButtonVisible) { @@ -269,6 +268,7 @@ private fun Bouncer( @Composable private fun UserInputArea( viewModel: BouncerViewModel, + visibility: UserInputAreaVisibility, modifier: Modifier = Modifier, ) { val authMethodViewModel: AuthMethodBouncerViewModel? by @@ -276,21 +276,46 @@ private fun UserInputArea( when (val nonNullViewModel = authMethodViewModel) { is PinBouncerViewModel -> - PinBouncer( - viewModel = nonNullViewModel, - modifier = modifier, - ) + when (visibility) { + UserInputAreaVisibility.FULL -> + PinBouncer( + viewModel = nonNullViewModel, + modifier = modifier, + ) + UserInputAreaVisibility.INPUT_ONLY -> + PinPad( + viewModel = nonNullViewModel, + modifier = modifier, + ) + UserInputAreaVisibility.OUTPUT_ONLY -> + PinInputDisplay( + viewModel = nonNullViewModel, + modifier = modifier, + ) + UserInputAreaVisibility.NONE -> {} + } is PasswordBouncerViewModel -> - PasswordBouncer( - viewModel = nonNullViewModel, - modifier = modifier, - ) + when (visibility) { + UserInputAreaVisibility.FULL, + UserInputAreaVisibility.INPUT_ONLY -> + PasswordBouncer( + viewModel = nonNullViewModel, + modifier = modifier, + ) + else -> {} + } is PatternBouncerViewModel -> - PatternBouncer( - viewModel = nonNullViewModel, - modifier = - Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier) - ) + when (visibility) { + UserInputAreaVisibility.FULL, + UserInputAreaVisibility.INPUT_ONLY -> + PatternBouncer( + viewModel = nonNullViewModel, + modifier = + Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false) + .then(modifier) + ) + else -> {} + } else -> Unit } } @@ -435,13 +460,14 @@ private fun Split( Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, - isUserInputAreaVisible = false, + userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY, modifier = startContentModifier, ) }, endContent = { endContentModifier -> UserInputArea( viewModel = viewModel, + visibility = UserInputAreaVisibility.INPUT_ONLY, modifier = endContentModifier, ) }, @@ -545,7 +571,7 @@ private fun SideBySide( Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, - isUserInputAreaVisible = true, + userInputAreaVisibility = UserInputAreaVisibility.FULL, modifier = endContentModifier, ) }, @@ -574,7 +600,7 @@ private fun Stacked( Bouncer( viewModel = viewModel, dialogFactory = dialogFactory, - isUserInputAreaVisible = true, + userInputAreaVisibility = UserInputAreaVisibility.FULL, modifier = Modifier.fillMaxWidth().weight(1f), ) } @@ -630,6 +656,27 @@ private enum class Layout { SPLIT, } +/** Enumerates all supported user-input area visibilities. */ +private enum class UserInputAreaVisibility { + /** + * The entire user input area is shown, including where the user enters input and where it's + * reflected to the user. + */ + FULL, + /** + * Only the area where the user enters the input is shown; the area where the input is reflected + * back to the user is not shown. + */ + INPUT_ONLY, + /** + * Only the area where the input is reflected back to the user is shown; the area where the + * input is entered by the user is not shown. + */ + OUTPUT_ONLY, + /** The entire user input area is hidden. */ + NONE, +} + /** * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of * the two reaches a stopping point but `0` in the middle of the transition. diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt index 6491b70db99b..84e016791816 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt @@ -55,12 +55,12 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.compose.grid.VerticalGrid import com.android.compose.modifiers.thenIf -import com.android.systemui.res.R import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon +import com.android.systemui.res.R import kotlin.time.Duration.Companion.milliseconds import kotlin.time.DurationUnit import kotlinx.coroutines.async @@ -93,7 +93,10 @@ internal fun PinBouncer( } @Composable -private fun PinPad(viewModel: PinBouncerViewModel) { +fun PinPad( + viewModel: PinBouncerViewModel, + modifier: Modifier = Modifier, +) { val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState() val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState() @@ -112,6 +115,7 @@ private fun PinPad(viewModel: PinBouncerViewModel) { columns = 3, verticalSpacing = 12.dp, horizontalSpacing = 20.dp, + modifier = modifier, ) { repeat(9) { index -> DigitButton( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt index 055ece328fc3..814ea31ad510 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt @@ -51,10 +51,10 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.keyguard.PinShapeAdapter -import com.android.systemui.res.R import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PinInputViewModel +import com.android.systemui.res.R import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async @@ -65,7 +65,10 @@ import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch @Composable -fun PinInputDisplay(viewModel: PinBouncerViewModel) { +fun PinInputDisplay( + viewModel: PinBouncerViewModel, + modifier: Modifier = Modifier, +) { val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState() val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes) @@ -81,8 +84,8 @@ fun PinInputDisplay(viewModel: PinBouncerViewModel) { // unifying into a single, more complex implementation. when (val length = hintedPinLength) { - null -> RegularPinInputDisplay(viewModel, shapeAnimations) - else -> HintingPinInputDisplay(viewModel, shapeAnimations, length) + null -> RegularPinInputDisplay(viewModel, shapeAnimations, modifier) + else -> HintingPinInputDisplay(viewModel, shapeAnimations, length, modifier) } } @@ -97,6 +100,7 @@ private fun HintingPinInputDisplay( viewModel: PinBouncerViewModel, shapeAnimations: ShapeAnimations, hintedPinLength: Int, + modifier: Modifier = Modifier, ) { val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState() // [ClearAll] marker pointing at the beginning of the current pin input. @@ -151,7 +155,7 @@ private fun HintingPinInputDisplay( LaunchedEffect(Unit) { playAnimation = true } val dotColor = MaterialTheme.colorScheme.onSurfaceVariant - Row(modifier = Modifier.heightIn(min = shapeAnimations.shapeSize)) { + Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) { pinEntryDrawable.forEachIndexed { index, drawable -> // Key the loop by [index] and [drawable], so that updating a shape drawable at the same // index will play the new animation (by remembering a new [atEnd]). @@ -183,6 +187,7 @@ private fun HintingPinInputDisplay( private fun RegularPinInputDisplay( viewModel: PinBouncerViewModel, shapeAnimations: ShapeAnimations, + modifier: Modifier = Modifier, ) { // Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from // `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove @@ -226,7 +231,7 @@ private fun RegularPinInputDisplay( } } - pinInputRow.Content() + pinInputRow.Content(modifier) } private class PinInputRow( @@ -235,10 +240,11 @@ private class PinInputRow( private val entries = mutableStateListOf<PinInputEntry>() @Composable - fun Content() { + fun Content(modifier: Modifier) { Row( modifier = - Modifier.heightIn(min = shapeAnimations.shapeSize) + modifier + .heightIn(min = shapeAnimations.shapeSize) // Pins overflowing horizontally should still be shown as scrolling. .wrapContentSize(unbounded = true), ) { diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8780f58743cd..9a3c6d5b322f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3266,4 +3266,9 @@ <string name="privacy_dialog_active_app_usage_2">In use by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string> <!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] --> <string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string> + + <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] --> + <string name="keyboard_backlight_dialog_title">Keyboard backlight</string> + <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] --> + <string name="keyboard_backlight_value">Level %1$d of %2$d</string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java index a14f97128662..f005af3780cb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java +++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java @@ -28,6 +28,7 @@ import android.graphics.PixelFormat; import android.graphics.RecordingCanvas; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.Looper; import android.os.Trace; import android.view.RenderNodeAnimator; import android.view.View; @@ -48,6 +49,7 @@ public class KeyButtonRipple extends Drawable { private static final float GLOW_MAX_ALPHA_DARK = 0.1f; private static final int ANIMATION_DURATION_SCALE = 350; private static final int ANIMATION_DURATION_FADE = 450; + private static final int ANIMATION_DURATION_FADE_FAST = 80; private static final Interpolator ALPHA_OUT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0.8f, 1f); @@ -71,6 +73,9 @@ public class KeyButtonRipple extends Drawable { private boolean mLastDark; private boolean mDark; private boolean mDelayTouchFeedback; + private boolean mSpeedUpNextFade; + // When non-null, this runs the next time this ripple is drawn invisibly. + private Runnable mOnInvisibleRunnable; private final Interpolator mInterpolator = new LogInterpolator(); private boolean mSupportHardware; @@ -112,6 +117,18 @@ public class KeyButtonRipple extends Drawable { mDelayTouchFeedback = delay; } + /** Next time we fade out (pressed==false), use a shorter duration than the standard. */ + public void speedUpNextFade() { + mSpeedUpNextFade = true; + } + + /** + * @param onInvisibleRunnable run after we are next drawn invisibly. Only used once. + */ + void setOnInvisibleRunnable(Runnable onInvisibleRunnable) { + mOnInvisibleRunnable = onInvisibleRunnable; + } + public void setType(Type type) { mType = type; } @@ -161,6 +178,11 @@ public class KeyButtonRipple extends Drawable { } else { drawSoftware(canvas); } + + if (!mPressed && !mVisible && mOnInvisibleRunnable != null) { + new Handler(Looper.getMainLooper()).post(mOnInvisibleRunnable); + mOnInvisibleRunnable = null; + } } @Override @@ -270,7 +292,7 @@ public class KeyButtonRipple extends Drawable { return true; } - public void setPressed(boolean pressed) { + private void setPressed(boolean pressed) { if (mDark != mLastDark && pressed) { mRipplePaint = null; mLastDark = mDark; @@ -350,7 +372,7 @@ public class KeyButtonRipple extends Drawable { private void exitSoftware() { ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f); alphaAnimator.setInterpolator(ALPHA_OUT_INTERPOLATOR); - alphaAnimator.setDuration(ANIMATION_DURATION_FADE); + alphaAnimator.setDuration(getFadeDuration()); alphaAnimator.addListener(mAnimatorListener); alphaAnimator.start(); mRunningAnimations.add(alphaAnimator); @@ -414,6 +436,12 @@ public class KeyButtonRipple extends Drawable { return Math.min(size, mMaxWidth); } + private int getFadeDuration() { + int duration = mSpeedUpNextFade ? ANIMATION_DURATION_FADE_FAST : ANIMATION_DURATION_FADE; + mSpeedUpNextFade = false; + return duration; + } + private void enterHardware() { endAnimations("enterHardware", true /* cancel */); mVisible = true; @@ -471,7 +499,7 @@ public class KeyButtonRipple extends Drawable { mPaintProp = CanvasProperty.createPaint(getRipplePaint()); final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp, RenderNodeAnimator.PAINT_ALPHA, 0); - opacityAnim.setDuration(ANIMATION_DURATION_FADE); + opacityAnim.setDuration(getFadeDuration()); opacityAnim.setInterpolator(ALPHA_OUT_INTERPOLATOR); opacityAnim.addListener(mAnimatorListener); opacityAnim.addListener(mExitHwTraceAnimator); diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 181aa5c1e648..f0915583f021 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -22,6 +22,7 @@ import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; +import static com.android.systemui.Flags.keyguardBottomAreaRefactor; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; @@ -459,7 +460,7 @@ public class LockIconViewController implements Dumpable { private void updateLockIconLocation() { final float scaleFactor = mAuthController.getScaleFactor(); final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); - if (mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { + if (keyguardBottomAreaRefactor()) { mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding, scaledPadding); } else { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 77384c49882a..dd971b966e7f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -244,15 +244,6 @@ object Flags { /** Keyguard Migration */ - /** - * Migrate the bottom area to the new keyguard root view. Because there is no such thing as a - * "bottom area" after this, this also breaks it up into many smaller, modular pieces. - */ - // TODO(b/290652751): Tracking bug. - @JvmField - val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA = - unreleasedFlag("migrate_split_keyguard_bottom_area") - // TODO(b/297037052): Tracking bug. @JvmField val REMOVE_NPVC_BOTTOM_AREA_USAGE = unreleasedFlag("remove_npvc_bottom_area_usage") @@ -264,10 +255,6 @@ object Flags { // TODO(b/287268101): Tracking bug. @JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock") - /** Migrate the lock icon view to the new keyguard root view. */ - // TODO(b/286552209): Tracking bug. - @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon") - // TODO(b/288276738): Tracking bug. @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard") @@ -494,12 +481,6 @@ object Flags { namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER, ) - // TODO(b/254512674): Tracking Bug - @Keep - @JvmField - val HIDE_NAVBAR_WINDOW = - sysPropBooleanFlag("persist.wm.debug.hide_navbar_window", default = false) - @Keep @JvmField val WM_CAPTION_ON_SHELL = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt index e16bb0bb8482..1e9be09bc3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt @@ -30,6 +30,7 @@ import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.Window import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout @@ -78,23 +79,29 @@ class KeyboardBacklightDialog( private lateinit var stepProperties: StepViewProperties @ColorInt - var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) + private val filledRectangleColor = + getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) @ColorInt - var emptyRectangleColor = + private val emptyRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant) @ColorInt - var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright) + private val backgroundColor = + getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright) @ColorInt - var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary) + private val defaultIconColor = + getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary) @ColorInt - var defaultIconBackgroundColor = + private val defaultIconBackgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) @ColorInt - var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface) + private val dimmedIconColor = + getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface) @ColorInt - var dimmedIconBackgroundColor = + private val dimmedIconBackgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim) + private val levelContentDescription = context.getString(R.string.keyboard_backlight_value) + init { currentLevel = initialCurrentLevel maxLevel = initialMaxLevel @@ -103,6 +110,8 @@ class KeyboardBacklightDialog( override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties(this) setWindowPosition() + // title is used for a11y announcement + window?.setTitle(context.getString(R.string.keyboard_backlight_dialog_title)) updateResources() rootView = buildRootView() setContentView(rootView) @@ -159,6 +168,12 @@ class KeyboardBacklightDialog( currentLevel = current updateIconTile() updateStepColors() + updateAccessibilityInfo() + } + + private fun updateAccessibilityInfo() { + rootView.contentDescription = String.format(levelContentDescription, currentLevel, maxLevel) + rootView.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) } private fun updateIconTile() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 61c8e1bbc1d8..1037b0eb4dfc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -27,6 +27,7 @@ import com.android.keyguard.LockIconView import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor @@ -113,7 +114,7 @@ constructor( fun bindIndicationArea() { indicationAreaHandle?.dispose() - if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let { keyguardRootView.removeView(it) } @@ -125,7 +126,6 @@ constructor( keyguardIndicationAreaViewModel, keyguardRootViewModel, indicationController, - featureFlags, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt index f20a66604ebf..1a8f62597037 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -22,9 +22,8 @@ import android.view.ViewGroup import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.lifecycle.repeatWhenAttached @@ -54,7 +53,6 @@ object KeyguardIndicationAreaBinder { viewModel: KeyguardIndicationAreaViewModel, keyguardRootViewModel: KeyguardRootViewModel, indicationController: KeyguardIndicationController, - featureFlags: FeatureFlags, ): DisposableHandle { val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area) indicationController.setIndicationArea(indicationArea) @@ -71,7 +69,7 @@ object KeyguardIndicationAreaBinder { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { keyguardRootViewModel.alpha.collect { alpha -> indicationArea.apply { this.importantForAccessibility = 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 378656c8f4c2..1f74bb661135 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 @@ -33,6 +33,7 @@ import com.android.app.animation.Interpolators import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.TintedIcon @@ -111,7 +112,7 @@ object KeyguardRootViewBinder { } } - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index b797c4b45445..bdd9a6bf3f79 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -41,6 +41,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isInvisible import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.animation.view.LaunchableImageView import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.broadcast.BroadcastDispatcher @@ -156,7 +157,7 @@ constructor( private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>() init { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { keyguardRootViewModel.enablePreviewMode() quickAffordancesCombinedViewModel.enablePreviewMode( initiallySelectedSlotId = @@ -199,7 +200,7 @@ constructor( setupKeyguardRootView(previewContext, rootView) - if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { setUpBottomArea(rootView) } @@ -243,7 +244,7 @@ constructor( } fun onSlotSelected(slotId: String) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId) } else { bottomAreaViewModel.onPreviewSlotSelected(slotId = slotId) @@ -254,7 +255,7 @@ constructor( isDestroyed = true lockscreenSmartspaceController.disconnect() disposables.forEach { it.dispose() } - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { shortcutsBindings.forEach { it.destroy() } } } @@ -363,7 +364,7 @@ constructor( disposables.add( PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { setupShortcuts(keyguardRootView) } setUpUdfps(previewContext, rootView) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt index 28e6a954a3d1..eb01d4f6f61c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt @@ -25,10 +25,9 @@ import androidx.constraintlayout.widget.ConstraintSet.LEFT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel @@ -41,7 +40,6 @@ class AlignShortcutsToUdfpsSection @Inject constructor( @Main private val resources: Resources, - private val featureFlags: FeatureFlags, private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, @@ -50,14 +48,14 @@ constructor( private val vibratorHelper: VibratorHelper, ) : BaseShortcutSection() { override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { addLeftShortcut(constraintLayout) addRightShortcut(constraintLayout) } } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { leftShortcutHandle = KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt index 9371d4e2d465..20cb9b0576db 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt @@ -29,9 +29,8 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel @@ -42,14 +41,13 @@ class DefaultAmbientIndicationAreaSection @Inject constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val featureFlags: FeatureFlags, private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, ) : KeyguardSection() { private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { val view = LayoutInflater.from(constraintLayout.context) .inflate(R.layout.ambient_indication, constraintLayout, false) @@ -59,7 +57,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { ambientIndicationAreaHandle = KeyguardAmbientIndicationAreaViewBinder.bind( constraintLayout, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt index 755549b5478b..ace970a01054 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt @@ -29,6 +29,7 @@ import androidx.constraintlayout.widget.ConstraintSet import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconView import com.android.keyguard.LockIconViewController +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.biometrics.AuthController import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -60,7 +61,7 @@ constructor( private val deviceEntryIconViewId = R.id.device_entry_icon_view override fun addViews(constraintLayout: ConstraintLayout) { - if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON) && + if (!keyguardBottomAreaRefactor() && !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS) ) { return @@ -74,7 +75,7 @@ constructor( if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId } } else { - // Flags.MIGRATE_LOCK_ICON + // keyguardBottomAreaRefactor() LockIconView(context, null).apply { id = R.id.lock_icon_view } } constraintLayout.addView(view) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt index 623eac013a35..8aef7c23b45d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt @@ -21,9 +21,8 @@ import android.content.Context import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea @@ -40,27 +39,25 @@ constructor( private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, private val indicationController: KeyguardIndicationController, - private val featureFlags: FeatureFlags, ) : KeyguardSection() { private val indicationAreaViewId = R.id.keyguard_indication_area private var indicationAreaHandle: DisposableHandle? = null override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { val view = KeyguardIndicationArea(context, null) constraintLayout.addView(view) } } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { indicationAreaHandle = KeyguardIndicationAreaBinder.bind( constraintLayout, keyguardIndicationAreaViewModel, keyguardRootViewModel, indicationController, - featureFlags, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt index 6fd13e0931aa..9a33f08386a3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt @@ -28,6 +28,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT import androidx.core.view.isVisible +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R import com.android.systemui.animation.view.LaunchableLinearLayout import com.android.systemui.dagger.qualifiers.Main @@ -45,7 +46,6 @@ class DefaultSettingsPopupMenuSection @Inject constructor( @Main private val resources: Resources, - private val featureFlags: FeatureFlags, private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel, private val vibratorHelper: VibratorHelper, private val activityStarter: ActivityStarter, @@ -53,7 +53,7 @@ constructor( private var settingsPopupMenuHandle: DisposableHandle? = null override fun addViews(constraintLayout: ConstraintLayout) { - if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { return } val view = @@ -68,7 +68,7 @@ constructor( } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { settingsPopupMenuHandle = KeyguardSettingsViewBinder.bind( constraintLayout.requireViewById<View>(R.id.keyguard_settings_button), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt index a67912017e54..0f6a966aad2e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt @@ -24,6 +24,7 @@ import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.LEFT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.res.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags @@ -40,7 +41,6 @@ class DefaultShortcutsSection @Inject constructor( @Main private val resources: Resources, - private val featureFlags: FeatureFlags, private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, @@ -49,14 +49,14 @@ constructor( private val vibratorHelper: VibratorHelper, ) : BaseShortcutSection() { override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { addLeftShortcut(constraintLayout) addRightShortcut(constraintLayout) } } override fun bindData(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { leftShortcutHandle = KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt index 980cc1b8ebe1..2327c028970b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt @@ -16,9 +16,8 @@ package com.android.systemui.keyguard.ui.viewmodel +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import javax.inject.Inject @@ -36,7 +35,6 @@ constructor( keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel, private val burnInHelperWrapper: BurnInHelperWrapper, private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, - private val featureFlags: FeatureFlags, ) { /** Notifies when a new configuration is set */ @@ -47,7 +45,7 @@ constructor( /** An observable for whether the indication area should be padded. */ val isIndicationAreaPadded: Flow<Boolean> = - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { combine(shortcutsCombinedViewModel.startButton, shortcutsCombinedViewModel.endButton) { startButtonModel, endButtonModel -> @@ -64,7 +62,7 @@ constructor( } /** An observable for the x-offset by which the indication area should be translated. */ val indicationAreaTranslationX: Flow<Float> = - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() } else { bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index a985236cb38e..5e3a166f5f35 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -1383,7 +1383,17 @@ public class NavigationBar extends ViewController<NavigationBarView> implements args.putInt( AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); - mAssistManagerLazy.get().startAssist(args); + // If Launcher has requested to override long press home, add a delay for the ripple. + // TODO(b/304146255): Remove this delay once we can exclude 3-button nav from screenshot. + boolean delayAssistInvocation = mAssistManagerLazy.get().shouldOverrideAssist( + AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); + // In practice, I think v should always be a KeyButtonView, but just being safe. + if (delayAssistInvocation && v instanceof KeyButtonView) { + ((KeyButtonView) v).setOnRippleInvisibleRunnable( + () -> mAssistManagerLazy.get().startAssist(args)); + } else { + mAssistManagerLazy.get().startAssist(args); + } mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams); mView.abortCurrentGesture(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java index 2928cceb35aa..79aedffc4498 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG; import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; +import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification; import android.content.Context; import android.content.pm.ActivityInfo; @@ -49,8 +50,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.settings.DisplayTracker; @@ -83,7 +82,6 @@ public class NavigationBarControllerImpl implements private final Context mContext; private final Handler mHandler; private final NavigationBarComponent.Factory mNavigationBarComponentFactory; - private FeatureFlags mFeatureFlags; private final SecureSettings mSecureSettings; private final DisplayTracker mDisplayTracker; private final DisplayManager mDisplayManager; @@ -118,13 +116,11 @@ public class NavigationBarControllerImpl implements TaskStackChangeListeners taskStackChangeListeners, Optional<Pip> pipOptional, Optional<BackAnimation> backAnimation, - FeatureFlags featureFlags, SecureSettings secureSettings, DisplayTracker displayTracker) { mContext = context; mHandler = mainHandler; mNavigationBarComponentFactory = navigationBarComponentFactory; - mFeatureFlags = featureFlags; mSecureSettings = secureSettings; mDisplayTracker = displayTracker; mDisplayManager = mContext.getSystemService(DisplayManager.class); @@ -248,8 +244,8 @@ public class NavigationBarControllerImpl implements /** @return {@code true} if taskbar is enabled, false otherwise */ private boolean initializeTaskbarIfNecessary() { // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen - boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled( - Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId()); + boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification()) + && shouldCreateNavBarAndTaskBar(mContext.getDisplayId()); if (taskbarEnabled) { Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary"); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index bc4f7f2513ce..258208d074e3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -62,7 +62,6 @@ import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.Gefingerpoken; -import com.android.systemui.res.R; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.navigationbar.buttons.ContextualButton; @@ -73,6 +72,7 @@ import com.android.systemui.navigationbar.buttons.NearestTouchFrame; import com.android.systemui.navigationbar.buttons.RotationContextButton; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.Recents; +import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.rotation.FloatingRotationButton; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java index dcf1a8e98f1c..6ec46f627264 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java @@ -58,8 +58,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; -import com.android.systemui.res.R; +import com.android.systemui.assist.AssistManager; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.res.R; import com.android.systemui.shared.system.QuickStepContract; public class KeyButtonView extends ImageView implements ButtonInterface { @@ -439,11 +440,22 @@ public class KeyButtonView extends ImageView implements ButtonInterface { if (mCode != KeyEvent.KEYCODE_UNKNOWN) { sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED); } + // When aborting long-press home and Launcher has requested to override it, fade out the + // ripple more quickly. + if (mCode == KeyEvent.KEYCODE_HOME && Dependency.get(AssistManager.class) + .shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) { + mRipple.speedUpNextFade(); + } setPressed(false); mRipple.abortDelayedRipple(); mGestureAborted = true; } + /** Run when the ripple for this button is next invisible. Only used once. */ + public void setOnRippleInvisibleRunnable(Runnable onRippleInvisibleRunnable) { + mRipple.setOnInvisibleRunnable(onRippleInvisibleRunnable); + } + @Override public void setDarkIntensity(float darkIntensity) { mDarkIntensity = darkIntensity; diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 093d098de3e3..d9a8080a1e83 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -325,7 +325,13 @@ constructor( } else { // TODO(b/278729185): Replace fire and forget service with a bounded service. val intent = NoteTaskControllerUpdateService.createIntent(context) - context.startServiceAsUser(intent, user) + try { + // If the user is stopped before 'startServiceAsUser' kicks-in, a + // 'SecurityException' will be thrown. + context.startServiceAsUser(intent, user) + } catch (e: SecurityException) { + debugLog(error = e) { "Unable to start 'NoteTaskControllerUpdateService'." } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt index 4dc1c82c5282..2074a14d323f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt @@ -24,7 +24,6 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.log.dagger.QSTilesLogBuffers import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.pipeline.shared.TileSpec -import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.statusbar.StatusBarState @@ -34,7 +33,7 @@ import javax.inject.Inject class QSTileLogger @Inject constructor( - @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>, + @QSTilesLogBuffers logBuffers: Map<String, LogBuffer>, private val factory: LogBufferFactory, private val mStatusBarStateController: StatusBarStateController, ) { @@ -163,22 +162,15 @@ constructor( private fun TileSpec.getLogBuffer(): LogBuffer = synchronized(logBufferCache) { - logBufferCache.getOrPut(this) { + logBufferCache.getOrPut(this.spec) { factory.create( - "QSTileLog_${this.getLogTag()}", + this.getLogTag(), BUFFER_MAX_SIZE /* maxSize */, false /* systrace */ ) } } - private fun DataUpdateTrigger.toLogString(): String = - when (this) { - is DataUpdateTrigger.ForceUpdate -> "force" - is DataUpdateTrigger.InitialRequest -> "init" - is DataUpdateTrigger.UserInput<*> -> input.action.toLogString() - } - private fun QSTileUserAction.toLogString(): String = when (this) { is QSTileUserAction.Click -> "click" @@ -198,7 +190,7 @@ constructor( "]" private companion object { - const val TAG_FORMAT_PREFIX = "QSLog" + const val TAG_FORMAT_PREFIX = "QSLog_tile_" const val DATA_MAX_LENGTH = 50 const val BUFFER_MAX_SIZE = 25 } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt index 0bee48fd01ab..12a083e990f8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt @@ -39,7 +39,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -47,6 +46,8 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.cancellable +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn @@ -57,6 +58,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Provides a hassle-free way to implement new tiles according to current System UI architecture @@ -83,10 +85,8 @@ class QSTileViewModelImpl<DATA_TYPE>( private val users: MutableStateFlow<UserHandle> = MutableStateFlow(userRepository.getSelectedUserInfo().userHandle) - private val userInputs: MutableSharedFlow<QSTileUserAction> = - MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) - private val forceUpdates: MutableSharedFlow<Unit> = - MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow() + private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow() private val spec get() = config.tileSpec @@ -130,7 +130,7 @@ class QSTileViewModelImpl<DATA_TYPE>( tileData.replayCache.isNotEmpty(), state.replayCache.isNotEmpty() ) - userInputs.tryEmit(userAction) + tileScope.launch { userInputs.emit(userAction) } } override fun destroy() { @@ -151,11 +151,16 @@ class QSTileViewModelImpl<DATA_TYPE>( emit(DataUpdateTrigger.InitialRequest) qsTileLogger.logInitialRequest(spec) } + .shareIn(tileScope, SharingStarted.WhileSubscribed()) tileDataInteractor() .tileData(user, updateTriggers) + // combine makes sure updateTriggers is always listened even if + // tileDataInteractor#tileData doesn't flatMapLatest on it + .combine(updateTriggers) { data, _ -> data } .cancellable() .flowOn(backgroundDispatcher) } + .distinctUntilChanged() .shareIn( tileScope, SharingStarted.WhileSubscribed(), @@ -171,8 +176,8 @@ class QSTileViewModelImpl<DATA_TYPE>( * * Subscribing to the result flow twice will result in doubling all actions, logs and analytics. */ - private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> { - return userInputs + private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> = + userInputs .filterFalseActions() .filterByPolicy(user) .throttle(CLICK_THROTTLE_DURATION, systemClock) @@ -187,7 +192,6 @@ class QSTileViewModelImpl<DATA_TYPE>( } .onEach { userActionInteractor().handleInput(it.input) } .flowOn(backgroundDispatcher) - } private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> = config.policy.let { policy -> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt index c4d7dfba23bf..18a4e2d26e89 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt @@ -36,9 +36,9 @@ data class QSTileConfig( */ sealed interface QSTileUIConfig { - val tileIconRes: Int + val iconRes: Int @DrawableRes get - val tileLabelRes: Int + val labelRes: Int @StringRes get /** @@ -46,16 +46,16 @@ sealed interface QSTileUIConfig { * of [Resource]. Returns [Resources.ID_NULL] for each field. */ data object Empty : QSTileUIConfig { - override val tileIconRes: Int + override val iconRes: Int get() = Resources.ID_NULL - override val tileLabelRes: Int + override val labelRes: Int get() = Resources.ID_NULL } /** Config containing actual icon and label resources. */ data class Resource( - @StringRes override val tileIconRes: Int, - @StringRes override val tileLabelRes: Int, + @DrawableRes override val iconRes: Int, + @StringRes override val labelRes: Int, ) : QSTileUIConfig } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt index dc5ccccd6f7f..30b87cc9e662 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.viewmodel +import android.content.Context import android.service.quicksettings.Tile import com.android.systemui.common.shared.model.Icon @@ -41,11 +42,19 @@ data class QSTileState( companion object { + fun build( + context: Context, + config: QSTileUIConfig, + build: Builder.() -> Unit + ): QSTileState = + build( + { Icon.Resource(config.iconRes, null) }, + context.getString(config.labelRes), + build, + ) + fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState = Builder(icon, label).apply(build).build() - - fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState = - build({ icon }, label, build) } enum class ActivationState(val legacyState: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt index efa6da764e6e..771d07c35cc3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt @@ -192,7 +192,7 @@ constructor( with(qsTileViewModel.config.uiConfig) { when (this) { is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: "" - is QSTileUIConfig.Resource -> context.getString(tileLabelRes) + is QSTileUIConfig.Resource -> context.getString(labelRes) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index 018f31b0c3d6..e40d2b7fd659 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -17,6 +17,7 @@ package com.android.systemui.scene.shared.flag import androidx.annotation.VisibleForTesting +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.Flags as AConfigFlags import com.android.systemui.Flags.sceneContainer import com.android.systemui.compose.ComposeFacade @@ -57,8 +58,6 @@ constructor( @VisibleForTesting val classicFlagTokens: List<Flag<Boolean>> = listOf( - Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, - Flags.MIGRATE_LOCK_ICON, Flags.MIGRATE_NSSL, Flags.MIGRATE_KEYGUARD_STATUS_VIEW, Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, @@ -72,6 +71,10 @@ constructor( flagName = AConfigFlags.FLAG_SCENE_CONTAINER, flagValue = sceneContainer(), ), + AconfigFlagMustBeEnabled( + flagName = AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, + flagValue = keyguardBottomAreaRefactor(), + ), ) + classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } + listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled()) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 85a4a7e45b32..823caa0805bd 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -24,6 +24,7 @@ import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; +import static com.android.systemui.Flags.keyguardBottomAreaRefactor; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -1070,7 +1071,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mQsController.init(); mShadeHeadsUpTracker.addTrackingHeadsUpListener( mNotificationStackScrollLayoutController::setTrackingHeadsUp); - if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area)); } @@ -1409,7 +1410,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump updateViewControllers(userAvatarView, keyguardUserSwitcherView); - if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { // Update keyguard bottom area int index = mView.indexOfChild(mKeyguardBottomArea); mView.removeView(mKeyguardBottomArea); @@ -1443,7 +1444,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mBarState); } - if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { setKeyguardBottomAreaVisibility(mBarState, false); } @@ -1456,7 +1457,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void initBottomArea() { - if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { mKeyguardBottomArea.init( mKeyguardBottomAreaViewModel, mFalsingManager, @@ -1643,7 +1644,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.setLockscreenClockY( mClockPositionAlgorithm.getExpandedPreferredClockY()); } - if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { mKeyguardInteractor.setClockPosition( mClockPositionResult.clockX, mClockPositionResult.clockY); } else { @@ -2710,7 +2711,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction()); alpha *= mBottomAreaShadeAlpha; - if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { mKeyguardInteractor.setAlpha(alpha); } else { mKeyguardBottomAreaInteractor.setAlpha(alpha); @@ -2936,7 +2937,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void updateDozingVisibilities(boolean animate) { - if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { mKeyguardInteractor.setAnimateDozingTransitions(animate); } else { mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate); @@ -3144,7 +3145,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mDozing = dozing; // TODO (b/) make listeners for this mNotificationStackScrollLayoutController.setDozing(mDozing, animate); - if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { mKeyguardInteractor.setAnimateDozingTransitions(animate); } else { mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate); @@ -4441,7 +4442,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump goingToFullShade, mBarState); - if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (!keyguardBottomAreaRefactor()) { setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); } @@ -4698,7 +4699,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.setAlpha(alpha); stackScroller.setAlpha(alpha); - if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + if (keyguardBottomAreaRefactor()) { mKeyguardInteractor.setAlpha(alpha); } else { mKeyguardBottomAreaInteractor.setAlpha(alpha); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 121aa425e67c..e9779cd02760 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -52,6 +52,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -59,6 +60,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.res.R; import com.android.systemui.scene.ui.view.WindowRootViewComponent; +import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; @@ -150,6 +152,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW ConfigurationController configurationController, KeyguardViewMediator keyguardViewMediator, KeyguardBypassController keyguardBypassController, + @Main Executor mainExecutor, @Background Executor backgroundExecutor, SysuiColorExtractor colorExtractor, DumpManager dumpManager, @@ -158,7 +161,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW AuthController authController, Lazy<ShadeInteractor> shadeInteractorLazy, ShadeWindowLogger logger, - Lazy<SelectedUserInteractor> userInteractor) { + Lazy<SelectedUserInteractor> userInteractor, + UserTracker userTracker) { mContext = context; mWindowRootViewComponentFactory = windowRootViewComponentFactory; mWindowManager = windowManager; @@ -184,7 +188,9 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); configurationController.addCallback(this); - + if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) { + userTracker.addCallback(mUserTrackerCallback, mainExecutor); + } float desiredPreferredRefreshRate = context.getResources() .getInteger(R.integer.config_keyguardRefreshRate); float actualPreferredRefreshRate = -1; @@ -572,6 +578,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW state.qsExpanded, state.headsUpNotificationShowing, state.lightRevealScrimOpaque, + state.isSwitchingUsers, state.forceWindowCollapsed, state.forceDozeBrightness, state.forceUserActivity, @@ -624,7 +631,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } private void applyHasTopUi(NotificationShadeWindowState state) { - mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state); + mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state) + || state.isSwitchingUsers; } private void applyNotTouchable(NotificationShadeWindowState state) { @@ -954,4 +962,24 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW setDreaming(isDreaming); } }; + + private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() { + @Override + public void onBeforeUserSwitching(int newUser) { + setIsSwitchingUsers(true); + } + + @Override + public void onUserChanged(int newUser, Context userContext) { + setIsSwitchingUsers(false); + } + + private void setIsSwitchingUsers(boolean isSwitchingUsers) { + if (mCurrentState.isSwitchingUsers == isSwitchingUsers) { + return; + } + mCurrentState.isSwitchingUsers = isSwitchingUsers; + apply(mCurrentState); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt index fbe164a8077f..0b20170834d8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt @@ -40,6 +40,7 @@ class NotificationShadeWindowState( @JvmField var qsExpanded: Boolean = false, @JvmField var headsUpNotificationShowing: Boolean = false, @JvmField var lightRevealScrimOpaque: Boolean = false, + @JvmField var isSwitchingUsers: Boolean = false, @JvmField var forceWindowCollapsed: Boolean = false, @JvmField var forceDozeBrightness: Boolean = false, // TODO: forceUserActivity seems to be unused, delete? @@ -78,6 +79,7 @@ class NotificationShadeWindowState( qsExpanded.toString(), headsUpNotificationShowing.toString(), lightRevealScrimOpaque.toString(), + isSwitchingUsers.toString(), forceWindowCollapsed.toString(), forceDozeBrightness.toString(), forceUserActivity.toString(), @@ -117,6 +119,7 @@ class NotificationShadeWindowState( qsExpanded: Boolean, headsUpShowing: Boolean, lightRevealScrimOpaque: Boolean, + isSwitchingUsers: Boolean, forceCollapsed: Boolean, forceDozeBrightness: Boolean, forceUserActivity: Boolean, @@ -145,6 +148,7 @@ class NotificationShadeWindowState( this.qsExpanded = qsExpanded this.headsUpNotificationShowing = headsUpShowing this.lightRevealScrimOpaque = lightRevealScrimOpaque + this.isSwitchingUsers = isSwitchingUsers this.forceWindowCollapsed = forceCollapsed this.forceDozeBrightness = forceDozeBrightness this.forceUserActivity = forceUserActivity @@ -191,6 +195,7 @@ class NotificationShadeWindowState( "qsExpanded", "headsUpShowing", "lightRevealScrimOpaque", + "isSwitchingUsers", "forceCollapsed", "forceDozeBrightness", "forceUserActivity", diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index 966ff35d0109..ec90a8d6ad59 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -90,10 +90,10 @@ public abstract class StackScrollerDecorView extends ExpandableView { } else { setWillBeGone(true); } - setContentVisible(visible, true /* animate */, null /* runAfter */); + setContentVisible(visible, true /* animate */, null /* onAnimationEnded */); } else { setVisibility(visible ? VISIBLE : GONE); - setContentVisible(visible, false /* animate */, null /* runAfter */); + setContentVisible(visible, false /* animate */, null /* onAnimationEnded */); setWillBeGone(false); notifyHeightChanged(false /* needsAnimation */); } @@ -108,7 +108,7 @@ public abstract class StackScrollerDecorView extends ExpandableView { * Change content visibility to {@code visible}, animated. */ public void setContentVisibleAnimated(boolean visible) { - setContentVisible(visible, true /* animate */, null /* runAfter */); + setContentVisible(visible, true /* animate */, null /* onAnimationEnded */); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java index 5c1149bafb2f..580431a13d1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java @@ -53,7 +53,7 @@ public class SectionHeaderView extends StackScrollerDecorView { mContents = requireViewById(R.id.content); bindContents(); super.onFinishInflate(); - setVisible(true /* nowVisible */, false /* animate */); + setVisible(true /* visible */, false /* animate */); } private void bindContents() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index d61ca697642a..1d4f2cbe6b64 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -21,7 +21,6 @@ import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; -import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -39,6 +38,7 @@ import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; @@ -148,9 +148,10 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); + mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(FACE_AUTH_REFACTOR, false); - mFeatureFlags.set(MIGRATE_LOCK_ICON, false); mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt new file mode 100644 index 000000000000..8b572eb3d906 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyboard.backlight.ui.view + +import android.testing.TestableLooper.RunWithLooper +import android.view.View +import android.view.accessibility.AccessibilityEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWithLooper +@SmallTest +@RunWith(JUnit4::class) +class KeyboardBacklightDialogTest : SysuiTestCase() { + + private lateinit var dialog: KeyboardBacklightDialog + private lateinit var rootView: View + private val descriptionString = context.getString(R.string.keyboard_backlight_value) + + @Before + fun setUp() { + dialog = + KeyboardBacklightDialog(context, initialCurrentLevel = 0, initialMaxLevel = MAX_LEVEL) + dialog.show() + rootView = dialog.requireViewById(R.id.keyboard_backlight_dialog_container) + } + + @Test + fun rootViewContentDescription_containsInitialLevel() { + assertThat(rootView.contentDescription).isEqualTo(contentDescriptionForLevel(INITIAL_LEVEL)) + } + + @Test + fun contentDescriptionUpdated_afterEveryLevelUpdate() { + val events = startCollectingAccessibilityEvents(rootView) + + dialog.updateState(current = 1, max = MAX_LEVEL) + + assertThat(rootView.contentDescription).isEqualTo(contentDescriptionForLevel(1)) + assertThat(events).contains(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) + } + + private fun contentDescriptionForLevel(level: Int): String { + return String.format(descriptionString, level, MAX_LEVEL) + } + + private fun startCollectingAccessibilityEvents(rootView: View): MutableList<Int> { + val events = mutableListOf<Int>() + rootView.accessibilityDelegate = + object : View.AccessibilityDelegate() { + override fun sendAccessibilityEvent(host: View, eventType: Int) { + super.sendAccessibilityEvent(host, eventType) + events.add(eventType) + } + } + return events + } + + companion object { + private const val MAX_LEVEL = 5 + private const val INITIAL_LEVEL = 0 + } +} 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 814a317a72f8..b16c3520d978 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -194,6 +194,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); + private FakeExecutor mUiMainExecutor = new FakeExecutor(new FakeSystemClock()); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private FalsingCollectorFake mFalsingCollector; @@ -247,6 +248,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mConfigurationController, mViewMediator, mKeyguardBypassController, + mUiMainExecutor, mUiBgExecutor, mColorExtractor, mDumpManager, @@ -255,7 +257,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mAuthController, () -> mShadeInteractor, mShadeWindowLogger, - () -> mSelectedUserInteractor); + () -> mSelectedUserInteractor, + mUserTracker); mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt index c7f7c3c3cecf..71313c8a5bf3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt @@ -26,6 +26,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags @@ -59,9 +60,11 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) + + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) + featureFlags = FakeFeatureFlagsClassic().apply { - set(Flags.MIGRATE_LOCK_ICON, false) set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) } @@ -81,7 +84,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateFlagOn() { - featureFlags.set(Flags.MIGRATE_LOCK_ICON, true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isGreaterThan(0) @@ -89,7 +92,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateAndRefactorFlagsOn() { - featureFlags.set(Flags.MIGRATE_LOCK_ICON, true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) @@ -98,7 +101,7 @@ class DefaultDeviceEntryIconSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateFlagOff() { - featureFlags.set(Flags.MIGRATE_LOCK_ICON, false) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt index 8b8c59b78e46..8dd33d5e60bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt @@ -23,12 +23,10 @@ import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.systemui.res.R import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.statusbar.KeyguardIndicationController -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -43,7 +41,6 @@ class DefaultIndicationAreaSectionTest : SysuiTestCase() { @Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel @Mock private lateinit var keyguardRootViewModel: KeyguardRootViewModel @Mock private lateinit var indicationController: KeyguardIndicationController - @Mock private lateinit var featureFlags: FeatureFlags private lateinit var underTest: DefaultIndicationAreaSection @@ -56,13 +53,12 @@ class DefaultIndicationAreaSectionTest : SysuiTestCase() { keyguardIndicationAreaViewModel, keyguardRootViewModel, indicationController, - featureFlags, ) } @Test fun addViewsConditionally() { - whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(true) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isGreaterThan(0) @@ -70,7 +66,7 @@ class DefaultIndicationAreaSectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateFlagOff() { - whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(false) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isEqualTo(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt index 34d93fc8788e..88a4aa509c37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt @@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.doze.util.BurnInHelperWrapper -import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory @@ -29,7 +28,6 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Before @@ -40,14 +38,12 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel - @Mock private lateinit var featureFlags: FeatureFlags private lateinit var underTest: KeyguardIndicationAreaViewModel private lateinit var repository: FakeKeyguardRepository @@ -87,7 +83,6 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { keyguardBottomAreaViewModel = bottomAreaViewModel, burnInHelperWrapper = burnInHelperWrapper, shortcutsCombinedViewModel = shortcutsCombinedViewModel, - featureFlags = featureFlags, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 1c6cc873c547..25d141997734 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dock.DockManagerFake +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys @@ -123,9 +124,11 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) dockManager = DockManagerFake() biometricSettingsRepository = FakeBiometricSettingsRepository() + + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) + val featureFlags = FakeFeatureFlags().apply { - set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true) set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 985b6fde4c63..259c74ff25fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FakeFeatureFlagsClassicModule import com.android.systemui.flags.Flags @@ -106,9 +107,10 @@ class KeyguardRootViewModelTest : SysuiTestCase() { testScope = TestScope(testDispatcher) MockitoAnnotations.initMocks(this) + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) + val featureFlags = FakeFeatureFlagsClassic().apply { - set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true) set(Flags.FACE_AUTH_REFACTOR, true) } @@ -351,7 +353,6 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() { featureFlags = FakeFeatureFlagsClassicModule { setDefault(Flags.NEW_AOD_TRANSITION) - set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true) set(Flags.FACE_AUTH_REFACTOR, true) }, mocks = diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java index c835146dd974..8a531fd9c842 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java @@ -111,7 +111,6 @@ public class NavigationBarControllerImplTest extends SysuiTestCase { TaskStackChangeListeners.getTestInstance(), Optional.of(mock(Pip.class)), Optional.of(mock(BackAnimation.class)), - mock(FeatureFlags.class), mock(SecureSettings.class), mDisplayTracker)); initializeNavigationBars(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 48a36cb5eb12..ddceed62fdeb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -27,6 +27,7 @@ import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowInsets.Type.ime; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; +import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS; import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; @@ -42,6 +43,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -86,6 +88,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.navigationbar.buttons.DeadZone; +import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; @@ -120,6 +123,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -143,6 +147,8 @@ public class NavigationBarTest extends SysuiTestCase { @Mock ButtonDispatcher mHomeButton; @Mock + KeyButtonView mHomeButtonView; + @Mock ButtonDispatcher mRecentsButton; @Mock ButtonDispatcher mAccessibilityButton; @@ -294,11 +300,38 @@ public class NavigationBarTest extends SysuiTestCase { @Test public void testHomeLongPress() { + when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) + .thenReturn(false); + mNavigationBar.init(); mNavigationBar.onViewAttached(); - mNavigationBar.onHomeLongClick(mNavigationBar.getView()); + mNavigationBar.onHomeLongClick(mHomeButtonView); verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS); + verify(mAssistManager).startAssist(any()); + } + + @Test + public void testHomeLongPressOverride() { + when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) + .thenReturn(true); + + mNavigationBar.init(); + mNavigationBar.onViewAttached(); + mNavigationBar.onHomeLongClick(mHomeButtonView); + + verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS); + + ArgumentCaptor<Runnable> onRippleInvisibleRunnableCaptor = ArgumentCaptor.forClass( + Runnable.class); + // startAssist is not called initially + verify(mAssistManager, never()).startAssist(any()); + // but a Runnable is added for when the ripple is invisible + verify(mHomeButtonView).setOnRippleInvisibleRunnable( + onRippleInvisibleRunnableCaptor.capture()); + // and when that runs, startAssist is called + onRippleInvisibleRunnableCaptor.getValue().run(); + verify(mAssistManager).startAssist(any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java index 078a917eb689..a1010a01f1e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java @@ -50,6 +50,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; +import com.android.systemui.assist.AssistManager; import com.android.systemui.recents.OverviewProxyService; import org.junit.Before; @@ -76,6 +77,7 @@ public class KeyButtonViewTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mDependency.injectMockDependency(OverviewProxyService.class); + mDependency.injectMockDependency(AssistManager.class); mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class); TestableLooper.get(this).runWithLooper(() -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt index 31d02ed78404..8f27e4e12d17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt @@ -57,7 +57,7 @@ class QSTileLoggerTest : SysuiTestCase() { whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer) underTest = QSTileLogger( - mapOf(TileSpec.create("chatty_tile") to chattyLogBuffer), + mapOf("chatty_tile" to chattyLogBuffer), logBufferFactory, statusBarController ) @@ -117,7 +117,7 @@ class QSTileLoggerTest : SysuiTestCase() { underTest.logUserActionPipeline( TileSpec.create("test_spec"), QSTileUserAction.Click(null), - QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}, + QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {}, "test_data", ) @@ -143,7 +143,7 @@ class QSTileLoggerTest : SysuiTestCase() { fun testLogStateUpdate() { underTest.logStateUpdate( TileSpec.create("test_spec"), - QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}, + QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {}, "test_data", ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt index 9bf4a759a1f2..d3b7daad792e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt @@ -97,7 +97,10 @@ class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() { { object : QSTileDataToStateMapper<Any> { override fun map(config: QSTileConfig, data: Any): QSTileState = - QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {} + QSTileState.build( + { Icon.Resource(0, ContentDescription.Resource(0)) }, + "" + ) {} } }, fakeDisabledByPolicyInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt index 32a38bd1faa1..4c8b56227efa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt @@ -67,6 +67,7 @@ internal class SceneContainerFlagsTest( listOf( AconfigFlags.FLAG_SCENE_CONTAINER, + AconfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, ) .forEach { flagToken -> setFlagsRule.enableFlags(flagToken) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index a7e1e9d35024..e6cd17f448b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -77,6 +77,7 @@ import com.android.systemui.scene.data.repository.SceneContainerRepository; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; +import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -136,8 +137,10 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private ShadeExpansionStateManager mShadeExpansionStateManager; @Mock private ShadeWindowLogger mShadeWindowLogger; @Mock private SelectedUserInteractor mSelectedUserInteractor; + @Mock private UserTracker mUserTracker; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; + private final Executor mMainExecutor = MoreExecutors.directExecutor(); private final Executor mBackgroundExecutor = MoreExecutors.directExecutor(); private SceneTestUtils mUtils = new SceneTestUtils(this); private TestScope mTestScope = mUtils.getTestScope(); @@ -261,6 +264,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, + mMainExecutor, mBackgroundExecutor, mColorExtractor, mDumpManager, @@ -269,7 +273,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mAuthController, () -> mShadeInteractor, mShadeWindowLogger, - () -> mSelectedUserInteractor) { + () -> mSelectedUserInteractor, + mUserTracker) { @Override protected boolean isDebuggable() { return false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 4f19742b26d9..a5806001dd43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -334,6 +334,8 @@ public class BubblesTest extends SysuiTestCase { @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock + private UserTracker mUserTracker; + @Mock private NotifPipelineFlags mNotifPipelineFlags; @Mock private Icon mAppBubbleIcon; @@ -488,6 +490,7 @@ public class BubblesTest extends SysuiTestCase { mKeyguardViewMediator, mKeyguardBypassController, syncExecutor, + syncExecutor, mColorExtractor, mDumpManager, mKeyguardStateController, @@ -495,7 +498,8 @@ public class BubblesTest extends SysuiTestCase { mAuthController, () -> mShadeInteractor, mShadeWindowLogger, - () -> mSelectedUserInteractor + () -> mSelectedUserInteractor, + mUserTracker ); mNotificationShadeWindowController.fetchWindowRootView(); mNotificationShadeWindowController.attach(); diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto index 25814eca9a85..9cda2672eab0 100644 --- a/proto/src/criticalevents/critical_event_log.proto +++ b/proto/src/criticalevents/critical_event_log.proto @@ -59,8 +59,11 @@ message CriticalEventProto { AppNotResponding anr = 4; JavaCrash java_crash = 5; NativeCrash native_crash = 6; + SystemServerStarted system_server_started = 7; } + message SystemServerStarted {} + message Watchdog { // The watchdog subject. // Required. diff --git a/services/core/Android.bp b/services/core/Android.bp index a14f3fee5303..4e49c6e4e7de 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -199,6 +199,7 @@ java_library_static { "biometrics_flags_lib", "am_flags_lib", "com_android_wm_shell_flags_lib", + "android.app.flags-aconfig-java" ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index e6cdbb58a9fd..599d99854b26 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -166,6 +166,7 @@ public class SettingsToPropertiesMapper { "safety_center", "sensors", "system_performance", + "system_sw_usb", "test_suites", "text", "threadnetwork", diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 87633e9e255d..47a99fe24ec4 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2030,6 +2030,9 @@ class UserController implements Handler.Callback { mTargetUserId = targetUserId; userSwitchUiEnabled = mUserSwitchUiEnabled; } + if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) { + mInjector.setHasTopUi(true); + } if (userSwitchUiEnabled) { UserInfo currentUserInfo = getUserInfo(currentUserId); Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); @@ -2098,6 +2101,9 @@ class UserController implements Handler.Callback { } private void endUserSwitch() { + if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) { + mInjector.setHasTopUi(false); + } final int nextUserId; synchronized (mLock) { nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL); @@ -3781,6 +3787,15 @@ class UserController implements Handler.Callback { getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId); } + void setHasTopUi(boolean hasTopUi) { + try { + Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi); + mService.setHasTopUi(hasTopUi); + } catch (RemoteException e) { + Slogf.e(TAG, "Failed to allow using all CPU cores", e); + } + } + void onSystemUserVisibilityChanged(boolean visible) { getUserManagerInternal().onSystemUserVisibilityChanged(visible); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 28970750a5ac..5d4f711b9432 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -70,7 +70,6 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -172,7 +171,7 @@ public class AudioDeviceBroker { @NonNull AudioSystemAdapter audioSystem) { mContext = context; mAudioService = service; - mBtHelper = new BtHelper(this); + mBtHelper = new BtHelper(this, context); mDeviceInventory = new AudioDeviceInventory(this); mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext); mAudioSystem = audioSystem; @@ -188,7 +187,7 @@ public class AudioDeviceBroker { @NonNull AudioSystemAdapter audioSystem) { mContext = context; mAudioService = service; - mBtHelper = new BtHelper(this); + mBtHelper = new BtHelper(this, context); mDeviceInventory = mockDeviceInventory; mSystemServer = mockSystemServer; mAudioSystem = audioSystem; @@ -1392,6 +1391,10 @@ public class AudioDeviceBroker { return mAudioService.hasAudioFocusUsers(); } + /*package*/ void postInitSpatializerHeadTrackingSensors() { + mAudioService.postInitSpatializerHeadTrackingSensors(); + } + //--------------------------------------------------------------------- // Message handling on behalf of helper classes. // Each of these methods posts a message to mBrokerHandler message queue. @@ -1475,6 +1478,15 @@ public class AudioDeviceBroker { sendLMsgNoDelay(MSG_L_RECEIVED_BT_EVENT, SENDMSG_QUEUE, intent); } + /*package*/ void postUpdateLeAudioGroupAddresses(int groupId) { + sendIMsgNoDelay( + MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES, SENDMSG_QUEUE, groupId); + } + + /*package*/ void postSynchronizeLeDevicesInInventory(AdiDeviceState deviceState) { + sendLMsgNoDelay(MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState); + } + /*package*/ static final class CommunicationDeviceInfo { final @NonNull IBinder mCb; // Identifies the requesting client for death handler final int mUid; // Requester UID @@ -1604,6 +1616,14 @@ public class AudioDeviceBroker { } } + /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) { + return mBtHelper.getLeAudioDeviceGroupId(device); + } + + /*package*/ List<String> getLeAudioGroupAddresses(int groupId) { + return mBtHelper.getLeAudioGroupAddresses(groupId); + } + /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent); } @@ -1976,6 +1996,22 @@ public class AudioDeviceBroker { onCheckCommunicationRouteClientState(msg.arg1, msg.arg2 == 1); } } break; + + case MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES: + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mDeviceInventory.onUpdateLeAudioGroupAddresses(msg.arg1); + } + } break; + + case MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY: + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mDeviceInventory.onSynchronizeLeDevicesInInventory( + (AdiDeviceState) msg.obj); + } + } break; + default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -2058,6 +2094,10 @@ public class AudioDeviceBroker { private static final int MSG_L_RECEIVED_BT_EVENT = 55; private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56; + private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57; + private static final int MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY = 58; + + private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { @@ -2582,9 +2622,9 @@ public class AudioDeviceBroker { } } - @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) { + List<String> getDeviceAddresses(AudioDeviceAttributes device) { synchronized (mDeviceStateLock) { - return mDeviceInventory.getDeviceSensorUuid(device); + return mDeviceInventory.getDeviceAddresses(device); } } @@ -2605,15 +2645,19 @@ public class AudioDeviceBroker { * in order to be mocked by a test a the same package * (see https://code.google.com/archive/p/mockito/issues/127) */ - public void persistAudioDeviceSettings() { + public void postPersistAudioDeviceSettings() { sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000); } void onPersistAudioDeviceSettings() { final String deviceSettings = mDeviceInventory.getDeviceSettings(); - Log.v(TAG, "saving AdiDeviceState: " + deviceSettings); - final SettingsAdapter settings = mAudioService.getSettings(); - boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(), + Log.v(TAG, "onPersistAudioDeviceSettings AdiDeviceState: " + deviceSettings); + String currentSettings = readDeviceSettings(); + if (deviceSettings.equals(currentSettings)) { + return; + } + final SettingsAdapter settingsAdapter = mAudioService.getSettings(); + boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(), Settings.Secure.AUDIO_DEVICE_INVENTORY, deviceSettings, UserHandle.USER_CURRENT); if (!res) { @@ -2621,11 +2665,17 @@ public class AudioDeviceBroker { } } - void onReadAudioDeviceSettings() { + private String readDeviceSettings() { final SettingsAdapter settingsAdapter = mAudioService.getSettings(); final ContentResolver contentResolver = mAudioService.getContentResolver(); - String settings = settingsAdapter.getSecureStringForUser(contentResolver, + return settingsAdapter.getSecureStringForUser(contentResolver, Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT); + } + + void onReadAudioDeviceSettings() { + final SettingsAdapter settingsAdapter = mAudioService.getSettings(); + final ContentResolver contentResolver = mAudioService.getContentResolver(); + String settings = readDeviceSettings(); if (settings == null) { Log.i(TAG, "reading AdiDeviceState from legacy key" + Settings.Secure.SPATIAL_AUDIO_ENABLED); @@ -2688,8 +2738,8 @@ public class AudioDeviceBroker { } @Nullable - AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) { - return mDeviceInventory.findBtDeviceStateForAddress(address, isBle); + AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) { + return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType); } //------------------------------------------------ diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e59fd77919c5..7ba0827f2016 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -15,14 +15,23 @@ */ package com.android.server.audio; +import static android.media.AudioSystem.DEVICE_IN_ALL_SCO_SET; import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET; import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET; +import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO_SET; +import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID; +import static android.media.AudioSystem.isBluetoothA2dpOutDevice; import static android.media.AudioSystem.isBluetoothDevice; +import static android.media.AudioSystem.isBluetoothLeOutDevice; +import static android.media.AudioSystem.isBluetoothOutDevice; +import static android.media.AudioSystem.isBluetoothScoOutDevice; + import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.media.AudioDeviceAttributes; @@ -72,7 +81,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.stream.Stream; /** @@ -118,6 +126,7 @@ public class AudioDeviceInventory { return oldState; }); } + mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState); } /** @@ -125,23 +134,28 @@ public class AudioDeviceInventory { * Bluetooth device and no corresponding entry already exists. * @param ada the device to add if needed */ - void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) { - if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) { + void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddres) { + if (!isBluetoothOutDevice(deviceType)) { return; } synchronized (mDeviceInventoryLock) { - if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) { + AdiDeviceState ads = findBtDeviceStateForAddress(address, deviceType); + if (ads == null) { + ads = findBtDeviceStateForAddress(peerAddres, deviceType); + } + if (ads != null) { + mDeviceBroker.postSynchronizeLeDevicesInInventory(ads); return; } - AdiDeviceState ads = new AdiDeviceState( - ada.getType(), ada.getInternalType(), ada.getAddress()); + ads = new AdiDeviceState(AudioDeviceInfo.convertInternalDeviceToDeviceType(deviceType), + deviceType, address); mDeviceInventory.put(ads.getDeviceId(), ads); + mDeviceBroker.postPersistAudioDeviceSettings(); } - mDeviceBroker.persistAudioDeviceSettings(); } /** - * Adds a new AdiDeviceState or updates the audio device cateogory of the matching + * Adds a new AdiDeviceState or updates the audio device category of the matching * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list. * @param deviceState the device to update */ @@ -152,6 +166,63 @@ public class AudioDeviceInventory { return oldState; }); } + mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState); + } + + /** + * synchronize AdiDeviceState for LE devices in the same group + */ + void onSynchronizeLeDevicesInInventory(AdiDeviceState updatedDevice) { + synchronized (mDevicesLock) { + synchronized (mDeviceInventoryLock) { + boolean found = false; + for (DeviceInfo di : mConnectedDevices.values()) { + if (di.mDeviceType != updatedDevice.getInternalDeviceType()) { + continue; + } + if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) { + for (AdiDeviceState ads2 : mDeviceInventory.values()) { + if (!(di.mDeviceType == ads2.getInternalDeviceType() + && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) { + continue; + } + ads2.setHasHeadTracker(updatedDevice.hasHeadTracker()); + ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled()); + ads2.setSAEnabled(updatedDevice.isSAEnabled()); + ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory()); + found = true; + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "onSynchronizeLeDevicesInInventory synced device pair ads1=" + + updatedDevice + " ads2=" + ads2).printLog(TAG)); + break; + } + } + if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) { + for (AdiDeviceState ads2 : mDeviceInventory.values()) { + if (!(di.mDeviceType == ads2.getInternalDeviceType() + && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) { + continue; + } + ads2.setHasHeadTracker(updatedDevice.hasHeadTracker()); + ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled()); + ads2.setSAEnabled(updatedDevice.isSAEnabled()); + ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory()); + found = true; + AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( + "onSynchronizeLeDevicesInInventory synced device pair ads1=" + + updatedDevice + " peer ads2=" + ads2).printLog(TAG)); + break; + } + } + if (found) { + break; + } + } + if (found) { + mDeviceBroker.postPersistAudioDeviceSettings(); + } + } + } } /** @@ -163,9 +234,21 @@ public class AudioDeviceInventory { * @return the found {@link AdiDeviceState} or {@code null} otherwise. */ @Nullable - AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) { + AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) { + Set<Integer> deviceSet; + if (isBluetoothA2dpOutDevice(deviceType)) { + deviceSet = DEVICE_OUT_ALL_A2DP_SET; + } else if (isBluetoothLeOutDevice(deviceType)) { + deviceSet = DEVICE_OUT_ALL_BLE_SET; + } else if (isBluetoothScoOutDevice(deviceType)) { + deviceSet = DEVICE_OUT_ALL_SCO_SET; + } else if (deviceType == DEVICE_OUT_HEARING_AID) { + deviceSet = new HashSet<>(); + deviceSet.add(DEVICE_OUT_HEARING_AID); + } else { + return null; + } synchronized (mDeviceInventoryLock) { - final Set<Integer> deviceSet = isBle ? DEVICE_OUT_ALL_BLE_SET : DEVICE_OUT_ALL_A2DP_SET; for (Integer internalType : deviceSet) { AdiDeviceState deviceState = mDeviceInventory.get( new Pair<>(internalType, address)); @@ -345,7 +428,8 @@ public class AudioDeviceInventory { final @NonNull String mDeviceName; final @NonNull String mDeviceAddress; int mDeviceCodecFormat; - final UUID mSensorUuid; + @NonNull String mPeerDeviceAddress; + final int mGroupId; /** Disabled operating modes for this device. Use a negative logic so that by default * an empty list means all modes are allowed. @@ -353,12 +437,13 @@ public class AudioDeviceInventory { @NonNull ArraySet<String> mDisabledModes = new ArraySet(0); DeviceInfo(int deviceType, String deviceName, String deviceAddress, - int deviceCodecFormat, @Nullable UUID sensorUuid) { + int deviceCodecFormat, String peerDeviceAddress, int groupId) { mDeviceType = deviceType; mDeviceName = deviceName == null ? "" : deviceName; mDeviceAddress = deviceAddress == null ? "" : deviceAddress; mDeviceCodecFormat = deviceCodecFormat; - mSensorUuid = sensorUuid; + mPeerDeviceAddress = peerDeviceAddress == null ? "" : peerDeviceAddress; + mGroupId = groupId; } void setModeDisabled(String mode) { @@ -379,7 +464,8 @@ public class AudioDeviceInventory { DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) { - this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null); + this(deviceType, deviceName, deviceAddress, deviceCodecFormat, + null, BluetoothLeAudio.GROUP_ID_INVALID); } DeviceInfo(int deviceType, String deviceName, String deviceAddress) { @@ -393,7 +479,8 @@ public class AudioDeviceInventory { + ") name:" + mDeviceName + " addr:" + mDeviceAddress + " codec: " + Integer.toHexString(mDeviceCodecFormat) - + " sensorUuid: " + Objects.toString(mSensorUuid) + + " peer addr:" + mPeerDeviceAddress + + " group:" + mGroupId + " disabled modes: " + mDisabledModes + "]"; } @@ -714,6 +801,27 @@ public class AudioDeviceInventory { } } + + /*package*/ void onUpdateLeAudioGroupAddresses(int groupId) { + synchronized (mDevicesLock) { + for (DeviceInfo di : mConnectedDevices.values()) { + if (di.mGroupId == groupId) { + List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId); + if (di.mPeerDeviceAddress.equals("")) { + for (String addr : addresses) { + if (!addr.equals(di.mDeviceAddress)) { + di.mPeerDeviceAddress = addr; + break; + } + } + } else if (!addresses.contains(di.mPeerDeviceAddress)) { + di.mPeerDeviceAddress = ""; + } + } + } + } + } + /*package*/ void onReportNewRoutes() { int n = mRoutesObservers.beginBroadcast(); if (n > 0) { @@ -1419,7 +1527,7 @@ public class AudioDeviceInventory { if (!connect) { purgeDevicesRoles_l(); } else { - addAudioDeviceInInventoryIfNeeded(attributes); + addAudioDeviceInInventoryIfNeeded(device, address, ""); } } mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); @@ -1477,7 +1585,7 @@ public class AudioDeviceInventory { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices mConnectedDevices.values().forEach(deviceInfo -> { - if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) { + if (deviceInfo.mDeviceType == DEVICE_OUT_HEARING_AID) { toRemove.add(deviceInfo.mDeviceAddress); } }); @@ -1485,8 +1593,8 @@ public class AudioDeviceInventory { .set(MediaMetrics.Property.EVENT, "disconnectHearingAid") .record(); if (toRemove.size() > 0) { - final int delay = checkSendBecomingNoisyIntentInt( - AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE); + final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID, + AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE); toRemove.stream().forEach(deviceAddress -> // TODO delay not used? makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/) @@ -1687,12 +1795,8 @@ public class AudioDeviceInventory { // Reset A2DP suspend state each time a new sink is connected mDeviceBroker.clearA2dpSuspended(true /* internalOnly */); - // The convention for head tracking sensors associated with A2DP devices is to - // use a UUID derived from the MAC address as follows: - // time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address - UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, - address, codec, sensorUuid); + address, codec); final String diKey = di.getKey(); mConnectedDevices.put(diKey, di); // on a connection always overwrite the device seen by AudioPolicy, see comment above when @@ -1703,7 +1807,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/); updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); - addAudioDeviceInInventoryIfNeeded(ada); + addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, ""); } static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER, @@ -1723,15 +1827,15 @@ public class AudioDeviceInventory { return; } DeviceInfo leOutDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET); + getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_BLE_SET); DeviceInfo leInDevice = getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET); DeviceInfo a2dpDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); + getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET); DeviceInfo scoOutDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET); + getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET); DeviceInfo scoInDevice = - getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET); + getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET); boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled()); boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled()) || (leInDevice != null && leInDevice.isDuplexModeEnabled()); @@ -1765,7 +1869,7 @@ public class AudioDeviceInventory { continue; } - if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) { + if (isBluetoothOutDevice(di.mDeviceType)) { for (AudioProductStrategy strategy : mStrategies) { boolean disable = false; if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) { @@ -1832,23 +1936,20 @@ public class AudioDeviceInventory { int checkProfileIsConnected(int profile) { switch (profile) { case BluetoothProfile.HEADSET: - if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null - || getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) { + if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET) != null + || getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET) != null) { return profile; } break; case BluetoothProfile.A2DP: - if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) { + if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET) != null) { return profile; } break; case BluetoothProfile.LE_AUDIO: case BluetoothProfile.LE_AUDIO_BROADCAST: if (getFirstConnectedDeviceOfTypes( - AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null + DEVICE_OUT_ALL_BLE_SET) != null || getFirstConnectedDeviceOfTypes( AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) { return profile; @@ -2006,28 +2107,28 @@ public class AudioDeviceInventory { private void makeHearingAidDeviceAvailable( String address, String name, int streamType, String eventSource) { final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, - AudioSystem.DEVICE_OUT_HEARING_AID); + DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); AudioDeviceAttributes ada = new AudioDeviceAttributes( - AudioSystem.DEVICE_OUT_HEARING_AID, address, name); + DEVICE_OUT_HEARING_AID, address, name); mAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( - DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), - new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address)); - mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID); + DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address), + new DeviceInfo(DEVICE_OUT_HEARING_AID, name, address)); + mDeviceBroker.postAccessoryPlugMediaUnmute(DEVICE_OUT_HEARING_AID); mDeviceBroker.postApplyVolumeOnDevice(streamType, - AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); + DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/); - addAudioDeviceInInventoryIfNeeded(ada); + addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, ""); new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable") .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID)) .set(MediaMetrics.Property.NAME, name) .set(MediaMetrics.Property.STREAM_TYPE, AudioSystem.streamToString(streamType)) @@ -2037,18 +2138,18 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { AudioDeviceAttributes ada = new AudioDeviceAttributes( - AudioSystem.DEVICE_OUT_HEARING_AID, address); + DEVICE_OUT_HEARING_AID, address); mAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( - DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); + DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address)); // Remove Hearing Aid routes as well setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable") .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID)) .record(); mDeviceBroker.postCheckCommunicationDeviceRemoval(ada); } @@ -2060,7 +2161,7 @@ public class AudioDeviceInventory { */ boolean isHearingAidConnected() { return getFirstConnectedDeviceOfTypes( - Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null; + Sets.newHashSet(DEVICE_OUT_HEARING_AID)) != null; } /** @@ -2102,6 +2203,20 @@ public class AudioDeviceInventory { final String address = btInfo.mDevice.getAddress(); String name = BtHelper.getName(btInfo.mDevice); + // Find LE Group ID and peer headset address if available + final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice); + String peerAddress = ""; + if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { + List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId); + if (addresses.size() > 1) { + for (String addr : addresses) { + if (!addr.equals(address)) { + peerAddress = addr; + break; + } + } + } + } // The BT Stack does not provide a name for LE Broadcast devices if (device == AudioSystem.DEVICE_OUT_BLE_BROADCAST && name.equals("")) { name = "Broadcast"; @@ -2127,14 +2242,12 @@ public class AudioDeviceInventory { } // Reset LEA suspend state each time a new sink is connected mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */); - - UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT, - sensorUuid)); + peerAddress, groupId)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false); - addAudioDeviceInInventoryIfNeeded(ada); + addAudioDeviceInInventoryIfNeeded(device, address, peerAddress); } if (streamType == AudioSystem.STREAM_DEFAULT) { @@ -2226,12 +2339,12 @@ public class AudioDeviceInventory { BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HDMI); BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE); - BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID); + BECOMING_NOISY_INTENT_DEVICES_SET.add(DEVICE_OUT_HEARING_AID); BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET); BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_BROADCAST); - BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); + BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_A2DP_SET); BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET); - BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET); + BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_BLE_SET); } // must be called before removing the device from mConnectedDevices @@ -2512,16 +2625,22 @@ public class AudioDeviceInventory { mDevRoleCapturePresetDispatchers.finishBroadcast(); } - @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) { + List<String> getDeviceAddresses(AudioDeviceAttributes device) { + List<String> addresses = new ArrayList<String>(); final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(), device.getAddress()); synchronized (mDevicesLock) { DeviceInfo di = mConnectedDevices.get(key); - if (di == null) { - return null; + if (di != null) { + if (!di.mDeviceAddress.isEmpty()) { + addresses.add(di.mDeviceAddress); + } + if (!di.mPeerDeviceAddress.isEmpty()) { + addresses.add(di.mPeerDeviceAddress); + } } - return di.mSensorUuid; } + return addresses; } /*package*/ String getDeviceSettings() { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d0ae0f25d6cd..99321c44931b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -237,7 +237,6 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.TreeSet; -import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -11054,7 +11053,9 @@ public class AudioService extends IAudioService.Stub final String addr = Objects.requireNonNull(address); - AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr, isBle); + AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr, + (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET + : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); int internalType = !isBle ? DEVICE_OUT_BLUETOOTH_A2DP : ((btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES) @@ -11070,7 +11071,7 @@ public class AudioService extends IAudioService.Stub deviceState.setAudioDeviceCategory(btAudioDeviceCategory); mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes()); mSoundDoseHelper.setAudioDeviceCategory(addr, internalType, @@ -11084,7 +11085,8 @@ public class AudioService extends IAudioService.Stub super.getBluetoothAudioDeviceCategory_enforcePermission(); final AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress( - Objects.requireNonNull(address), isBle); + Objects.requireNonNull(address), (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET + : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); if (deviceState == null) { return AUDIO_DEVICE_CATEGORY_UNKNOWN; } @@ -13596,8 +13598,8 @@ public class AudioService extends IAudioService.Stub return activeAssistantUids; } - UUID getDeviceSensorUuid(AudioDeviceAttributes device) { - return mDeviceBroker.getDeviceSensorUuid(device); + List<String> getDeviceAddresses(AudioDeviceAttributes device) { + return mDeviceBroker.getDeviceAddresses(device); } //====================== diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index cce6bd2938d1..7b9621581adf 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -26,7 +26,9 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; +import android.bluetooth.BluetoothLeAudioCodecStatus; import android.bluetooth.BluetoothProfile; +import android.content.Context; import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; @@ -43,6 +45,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.server.utils.EventLogger; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,9 +60,11 @@ public class BtHelper { private static final String TAG = "AS.BtHelper"; private final @NonNull AudioDeviceBroker mDeviceBroker; + private final @NonNull Context mContext; - BtHelper(@NonNull AudioDeviceBroker broker) { + BtHelper(@NonNull AudioDeviceBroker broker, Context context) { mDeviceBroker = broker; + mContext = context; } // BluetoothHeadset API to control SCO connection @@ -498,6 +503,32 @@ public class BtHelper { } } + // BluetoothLeAudio callback used to update the list of addresses in the same group as a + // connected LE Audio device + MyLeAudioCallback mLeAudioCallback = null; + + class MyLeAudioCallback implements BluetoothLeAudio.Callback { + @Override + public void onCodecConfigChanged(int groupId, + @NonNull BluetoothLeAudioCodecStatus status) { + // Do nothing + } + + @Override + public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) { + mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); + } + + @Override + public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) { + mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); + } + @Override + public void onGroupStatusChanged(int groupId, int groupStatus) { + mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); + } + } + // @GuardedBy("mDeviceBroker.mSetModeLock") @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { @@ -519,6 +550,11 @@ public class BtHelper { mHearingAid = (BluetoothHearingAid) proxy; break; case BluetoothProfile.LE_AUDIO: + if (mLeAudio == null) { + mLeAudioCallback = new MyLeAudioCallback(); + ((BluetoothLeAudio) proxy).registerCallback( + mContext.getMainExecutor(), mLeAudioCallback); + } mLeAudio = (BluetoothLeAudio) proxy; break; case BluetoothProfile.A2DP_SINK: @@ -977,6 +1013,28 @@ public class BtHelper { return result; } + /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) { + if (mLeAudio == null || device == null) { + return BluetoothLeAudio.GROUP_ID_INVALID; + } + return mLeAudio.getGroupId(device); + } + + /*package*/ List<String> getLeAudioGroupAddresses(int groupId) { + List<String> addresses = new ArrayList<String>(); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter == null || mLeAudio == null) { + return addresses; + } + List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO); + for (BluetoothDevice device : activeDevices) { + if (device != null && mLeAudio.getGroupId(device) == groupId) { + addresses.add(device.getAddress()); + } + } + return addresses; + } + /** * Returns the String equivalent of the btCodecType. * diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 7abd9c7f750b..ea92154f2df0 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -564,7 +564,7 @@ public class SpatializerHelper { } if (updatedDevice != null) { onRoutingUpdated(); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); logDeviceState(updatedDevice, "addCompatibleAudioDevice"); } } @@ -614,7 +614,7 @@ public class SpatializerHelper { if (deviceState != null && deviceState.isSAEnabled()) { deviceState.setSAEnabled(false); onRoutingUpdated(); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); logDeviceState(deviceState, "removeCompatibleAudioDevice"); } } @@ -716,7 +716,7 @@ public class SpatializerHelper { ada.getAddress()); initSAState(deviceState); mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later. } } @@ -1206,7 +1206,7 @@ public class SpatializerHelper { } Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada); deviceState.setHeadTrackerEnabled(enabled); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); logDeviceState(deviceState, "setHeadTrackerEnabled"); // check current routing to see if it affects the headtracking mode @@ -1248,7 +1248,7 @@ public class SpatializerHelper { if (deviceState != null) { if (!deviceState.hasHeadTracker()) { deviceState.setHasHeadTracker(true); - mDeviceBroker.persistAudioDeviceSettings(); + mDeviceBroker.postPersistAudioDeviceSettings(); logDeviceState(deviceState, "setHasHeadTracker"); } return deviceState.isHeadTrackerEnabled(); @@ -1631,25 +1631,33 @@ public class SpatializerHelper { return headHandle; } final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0); - UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(currentDevice); + List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice); + // We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion // with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR // and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by // SensorPoseProvider). // Note: this is a dynamic sensor list right now. List<Sensor> sensors = mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER); - for (Sensor sensor : sensors) { - final UUID uuid = sensor.getUuid(); - if (uuid.equals(routingDeviceUuid)) { - headHandle = sensor.getHandle(); - if (!setHasHeadTracker(currentDevice)) { - headHandle = -1; + for (String address : deviceAddresses) { + UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes( + new AudioDeviceAttributes(currentDevice.getInternalType(), address)); + for (Sensor sensor : sensors) { + final UUID uuid = sensor.getUuid(); + if (uuid.equals(routingDeviceUuid)) { + headHandle = sensor.getHandle(); + if (!setHasHeadTracker(currentDevice)) { + headHandle = -1; + } + break; + } + if (uuid.equals(UuidUtils.STANDALONE_UUID)) { + headHandle = sensor.getHandle(); + // we do not break, perhaps we find a head tracker on device. } - break; } - if (uuid.equals(UuidUtils.STANDALONE_UUID)) { - headHandle = sensor.getHandle(); - // we do not break, perhaps we find a head tracker on device. + if (headHandle != -1) { + break; } } return headHandle; diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java index ab480e8e8852..08143759fab4 100644 --- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java +++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java @@ -31,6 +31,7 @@ import com.android.server.criticalevents.nano.CriticalEventProto.AppNotRespondin import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog; import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash; import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash; +import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted; import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog; import java.io.File; @@ -141,6 +142,13 @@ public class CriticalEventLog { return System.currentTimeMillis(); } + /** Logs when system server started. */ + public void logSystemServerStarted() { + CriticalEventProto event = new CriticalEventProto(); + event.setSystemServerStarted(new SystemServerStarted()); + log(event); + } + /** Logs a watchdog. */ public void logWatchdog(String subject, UUID uuid) { Watchdog watchdog = new Watchdog(); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 71562dc1ed86..9802adf302d1 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -26,6 +26,7 @@ import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import android.app.AppOpsManager; import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.Notification; import android.app.NotificationManager; import android.app.NotificationManager.Policy; @@ -670,14 +671,37 @@ public class ZenModeHelper { if (rule.enabled != automaticZenRule.isEnabled()) { rule.snoozing = false; } + if (Flags.modesApi()) { + rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed(); + rule.iconResId = automaticZenRule.getIconResId(); + rule.triggerDescription = automaticZenRule.getTriggerDescription(); + rule.type = automaticZenRule.getType(); + } } protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) { - AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, - rule.configurationActivity, - rule.conditionId, rule.zenPolicy, - NotificationManager.zenModeToInterruptionFilter(rule.zenMode), - rule.enabled, rule.creationTime); + AutomaticZenRule azr; + if (Flags.modesApi()) { + azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId) + .setManualInvocationAllowed(rule.allowManualInvocation) + .setCreationTime(rule.creationTime) + .setIconResId(rule.iconResId) + .setType(rule.type) + .setZenPolicy(rule.zenPolicy) + .setEnabled(rule.enabled) + .setInterruptionFilter( + NotificationManager.zenModeToInterruptionFilter(rule.zenMode)) + .setOwner(rule.component) + .setConfigurationActivity(rule.configurationActivity) + .setTriggerDescription(rule.triggerDescription) + .build(); + } else { + azr = new AutomaticZenRule(rule.name, rule.component, + rule.configurationActivity, + rule.conditionId, rule.zenPolicy, + NotificationManager.zenModeToInterruptionFilter(rule.zenMode), + rule.enabled, rule.creationTime); + } azr.setPackageName(rule.pkg); return azr; } @@ -713,6 +737,9 @@ public class ZenModeHelper { newRule.zenMode = zenMode; newRule.conditionId = conditionId; newRule.enabler = caller; + if (Flags.modesApi()) { + newRule.allowManualInvocation = true; + } newConfig.manualRule = newRule; } setConfigLocked(newConfig, reason, null, setRingerMode, callingUid, diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING index f1bfea760792..5c37680af745 100644 --- a/services/core/java/com/android/server/timedetector/TEST_MAPPING +++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING @@ -1,14 +1,6 @@ { "presubmit": [ { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.timedetector." - } - ] - }, - { "name": "CtsTimeTestCases", "options": [ { @@ -16,5 +8,11 @@ } ] } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ + { + "name": "FrameworksTimeServicesTests" + } ] } diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING index 455accdd198a..63dd7b42f23b 100644 --- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING +++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING @@ -1,14 +1,6 @@ { "presubmit": [ - { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.timezonedetector." - } - ] - }, - { + { "name": "CtsTimeTestCases", "options": [ { @@ -21,6 +13,9 @@ "postsubmit": [ { "name": "CtsLocationTimeZoneManagerHostTest" + }, + { + "name": "FrameworksTimeServicesTests" } ] } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 4a5311b14397..2c492035140b 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -269,12 +269,27 @@ public class ActivityStartController { } } + /** + * Start intent as a package. + * + * @param uid Make a call as if this UID did. + * @param callingPackage Make a call as if this package did. + * @param callingFeatureId Make a call as if this feature in the package did. + * @param intent Intent to start. + * @param userId Start the intents on this user. + * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. + * @param originatingPendingIntent PendingIntentRecord that originated this activity start or + * null if not originated by PendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. + */ final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid, String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid, reason); @@ -295,7 +310,7 @@ public class ActivityStartController { .setUserId(userId) .setInTask(inTask) .setOriginatingPendingIntent(originatingPendingIntent) - .setBackgroundStartPrivileges(backgroundStartPrivileges) + .setBackgroundStartPrivileges(forcedBalByPiSender) .execute(); } @@ -310,15 +325,18 @@ public class ActivityStartController { * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. * @param originatingPendingIntent PendingIntentRecord that originated this activity start or * null if not originated by PendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. */ final int startActivitiesInPackage(int uid, String callingPackage, @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */, callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId, - validateIncomingUser, originatingPendingIntent, backgroundStartPrivileges); + validateIncomingUser, originatingPendingIntent, forcedBalByPiSender); } /** @@ -333,12 +351,15 @@ public class ActivityStartController { * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. * @param originatingPendingIntent PendingIntentRecord that originated this activity start or * null if not originated by PendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. */ final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid, String callingPackage, @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { final String reason = "startActivityInPackage"; @@ -348,14 +369,14 @@ public class ActivityStartController { // TODO: Switch to user app stacks here. return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason, - originatingPendingIntent, backgroundStartPrivileges); + originatingPendingIntent, forcedBalByPiSender); } int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid, int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, String reason, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { if (intents == null) { throw new NullPointerException("intents is null"); } @@ -463,7 +484,7 @@ public class ActivityStartController { // top one as otherwise an activity below might consume it. .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/) .setOriginatingPendingIntent(originatingPendingIntent) - .setBackgroundStartPrivileges(backgroundStartPrivileges); + .setBackgroundStartPrivileges(forcedBalByPiSender); } // Log if the activities to be started have different uids. if (startingUidPkgs.size() > 1) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 34bf8edc148f..009b8e048840 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -388,7 +388,7 @@ class ActivityStarter { WaitResult waitResult; int filterCallingUid; PendingIntentRecord originatingPendingIntent; - BackgroundStartPrivileges backgroundStartPrivileges; + BackgroundStartPrivileges forcedBalByPiSender; final StringBuilder logMessage = new StringBuilder(); @@ -451,7 +451,7 @@ class ActivityStarter { allowPendingRemoteAnimationRegistryLookup = true; filterCallingUid = UserHandle.USER_NULL; originatingPendingIntent = null; - backgroundStartPrivileges = BackgroundStartPrivileges.NONE; + forcedBalByPiSender = BackgroundStartPrivileges.NONE; errorCallbackToken = null; } @@ -494,7 +494,7 @@ class ActivityStarter { = request.allowPendingRemoteAnimationRegistryLookup; filterCallingUid = request.filterCallingUid; originatingPendingIntent = request.originatingPendingIntent; - backgroundStartPrivileges = request.backgroundStartPrivileges; + forcedBalByPiSender = request.forcedBalByPiSender; errorCallbackToken = request.errorCallbackToken; } @@ -1106,7 +1106,7 @@ class ActivityStarter { realCallingPid, callerApp, request.originatingPendingIntent, - request.backgroundStartPrivileges, + request.forcedBalByPiSender, intent, checkedOptions); balCode = balVerdict.getCode(); @@ -3167,9 +3167,8 @@ class ActivityStarter { return this; } - ActivityStarter setBackgroundStartPrivileges( - BackgroundStartPrivileges backgroundStartPrivileges) { - mRequest.backgroundStartPrivileges = backgroundStartPrivileges; + ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) { + mRequest.forcedBalByPiSender = forcedBalByPiSender; return this; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index a2547fd437d1..5604b1a6aa39 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -215,21 +215,39 @@ public abstract class ActivityTaskManagerInternal { * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. * @param originatingPendingIntent PendingIntentRecord that originated this activity start or * null if not originated by PendingIntent - * @param allowBackgroundActivityStart Whether the background activity start should be allowed - * from originatingPendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. */ public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid, String callingPackage, @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges); + BackgroundStartPrivileges forcedBalByPiSender); + /** + * Start intent as a package. + * + * @param uid Make a call as if this UID did. + * @param realCallingPid PID of the real caller. + * @param realCallingUid UID of the real caller. + * @param callingPackage Make a call as if this package did. + * @param callingFeatureId Make a call as if this feature in the package did. + * @param intent Intent to start. + * @param userId Start the intents on this user. + * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. + * @param originatingPendingIntent PendingIntentRecord that originated this activity start or + * null if not originated by PendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. + */ public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid, - String callingPackage, @Nullable String callingFeaturId, Intent intent, + String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges); + BackgroundStartPrivileges forcedBalByPiSender); /** * Callback to be called on certain activity start scenarios. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f462efc00ce6..a76fe287f313 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5911,12 +5911,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String callingPackage, @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { assertPackageMatchesCallingUid(callingPackage); return getActivityStartController().startActivitiesInPackage(uid, realCallingPid, realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId, validateIncomingUser, originatingPendingIntent, - backgroundStartPrivileges); + forcedBalByPiSender); } @Override @@ -5925,13 +5925,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges) { + BackgroundStartPrivileges forcedBalByPiSender) { assertPackageMatchesCallingUid(callingPackage); return getActivityStartController().startActivityInPackage(uid, realCallingPid, realCallingUid, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, options, userId, inTask, reason, validateIncomingUser, originatingPendingIntent, - backgroundStartPrivileges); + forcedBalByPiSender); } @Override diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 2f9ef50297bf..1b5631f59a3e 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -195,6 +195,10 @@ public class BackgroundActivityStartController { return activity != null && packageName.equals(activity.getPackageName()); } + /** + * @see #checkBackgroundActivityStart(int, int, String, int, int, WindowProcessController, + * PendingIntentRecord, BackgroundStartPrivileges, Intent, ActivityOptions) + */ boolean shouldAbortBackgroundActivityStart( int callingUid, int callingPid, @@ -203,13 +207,13 @@ public class BackgroundActivityStartController { int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges, + BackgroundStartPrivileges forcedBalByPiSender, Intent intent, ActivityOptions checkedOptions) { return checkBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, - backgroundStartPrivileges, intent, checkedOptions).blocks(); + forcedBalByPiSender, intent, checkedOptions).blocks(); } private class BalState { @@ -230,7 +234,7 @@ public class BackgroundActivityStartController { private final @ActivityManager.ProcessState int mRealCallingUidProcState; private final boolean mIsRealCallingUidPersistentSystemProcess; private final PendingIntentRecord mOriginatingPendingIntent; - private final BackgroundStartPrivileges mBackgroundStartPrivileges; + private final BackgroundStartPrivileges mForcedBalByPiSender; private final Intent mIntent; private final WindowProcessController mCallerApp; private final WindowProcessController mRealCallerApp; @@ -239,7 +243,7 @@ public class BackgroundActivityStartController { int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges, + BackgroundStartPrivileges forcedBalByPiSender, Intent intent, ActivityOptions checkedOptions) { this.mCallingPackage = callingPackage; @@ -248,7 +252,7 @@ public class BackgroundActivityStartController { mRealCallingUid = realCallingUid; mRealCallingPid = realCallingPid; mCallerApp = callerApp; - mBackgroundStartPrivileges = backgroundStartPrivileges; + mForcedBalByPiSender = forcedBalByPiSender; mOriginatingPendingIntent = originatingPendingIntent; mIntent = intent; mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid); @@ -344,7 +348,7 @@ public class BackgroundActivityStartController { .append(mIsRealCallingUidPersistentSystemProcess); sb.append("; originatingPendingIntent: ").append(mOriginatingPendingIntent); } - sb.append("; backgroundStartPrivileges: ").append(mBackgroundStartPrivileges); + sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender); sb.append("; intent: ").append(mIntent); sb.append("; callerApp: ").append(mCallerApp); if (isPendingIntent()) { @@ -422,8 +426,26 @@ public class BackgroundActivityStartController { } /** - * @return A code denoting which BAL rule allows an activity to be started, - * or {@link #BAL_BLOCK} if the launch should be blocked + * Check if a (background) activity start is allowed. + * + * @param callingUid The UID that wants to start the activity. + * @param callingPid The PID that wants to start the activity. + * @param callingPackage The package name that wants to start the activity. + * @param realCallingUid The UID that actually calls this method (only if this handles a + * PendingIntent, otherwise -1) + * @param realCallingPid The PID that actually calls this method (only if this handles a + * * PendingIntent, otherwise -1) + * @param callerApp The process that calls this method (only if not a PendingIntent) + * @param originatingPendingIntent PendingIntentRecord that originated this activity start or + * null if not originated by PendingIntent + * @param forcedBalByPiSender If set to allow, the + * PendingIntent's sender will try to force allow background activity starts. + * This is only possible if the sender of the PendingIntent is a system process. + * @param intent Intent that should be started. + * @param checkedOptions ActivityOptions to allow specific opt-ins/opt outs. + * + * @return A verdict denoting which BAL rule allows an activity to be started, + * or if the launch should be blocked. */ BalVerdict checkBackgroundActivityStart( int callingUid, @@ -433,7 +455,7 @@ public class BackgroundActivityStartController { int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges backgroundStartPrivileges, + BackgroundStartPrivileges forcedBalByPiSender, Intent intent, ActivityOptions checkedOptions) { @@ -444,7 +466,7 @@ public class BackgroundActivityStartController { BalState state = new BalState(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, - backgroundStartPrivileges, intent, checkedOptions); + forcedBalByPiSender, intent, checkedOptions); // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a // visible window. @@ -708,12 +730,12 @@ public class BackgroundActivityStartController { } // if the realCallingUid is a persistent system process, abort if the IntentSender // wasn't allowed to start an activity - if (state.mIsRealCallingUidPersistentSystemProcess - && state.mBackgroundStartPrivileges.allowsBackgroundActivityStarts()) { + if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts() + && state.mIsRealCallingUidPersistentSystemProcess) { return new BalVerdict(BAL_ALLOW_PENDING_INTENT, /*background*/ false, "realCallingUid is persistent system process AND intent " - + "sender allowed (allowBackgroundActivityStart = true)."); + + "sender forced to allow."); } // don't abort if the realCallingUid is an associated companion app if (mService.isAssociatedCompanionApp( diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 00f2b8963350..2f52de4cb765 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -274,11 +274,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { boolean mClearedForReorderActivityToFront; /** - * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}. - */ - boolean mIsSurfaceManagedBySystemOrganizer = false; - - /** * When we are in the process of pausing an activity, before starting the * next one, this variable holds the activity that is currently being paused. * @@ -453,21 +448,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, @NonNull String processName) { - setTaskFragmentOrganizer(organizer, uid, processName, - false /* isSurfaceManagedBySystemOrganizer */); - } - - void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, - @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) { mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder()); mTaskFragmentOrganizerUid = uid; mTaskFragmentOrganizerProcessName = processName; - mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer; } void onTaskFragmentOrganizerRemoved() { mTaskFragmentOrganizer = null; - mIsSurfaceManagedBySystemOrganizer = false; } /** Whether this TaskFragment is organized by the given {@code organizer}. */ @@ -2454,9 +2441,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) { return; } - if (mIsSurfaceManagedBySystemOrganizer) { - return; - } if (mTransitionController.isShellTransitionsEnabled() && !mTransitionController.isCollecting(this)) { // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 95e25151e8cc..89d47bcf41d5 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -2122,8 +2122,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // actions. TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer(); taskFragment.setTaskFragmentOrganizer(organizerToken, - ownerActivity.getUid(), ownerActivity.info.processName, - mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder())); + ownerActivity.getUid(), ownerActivity.info.processName); final int position; if (creationParams.getPairedPrimaryFragmentToken() != null) { // When there is a paired primary TaskFragment, we want to place the new TaskFragment diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 56e385d535a0..0a2e80606e96 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -129,6 +129,7 @@ import com.android.server.connectivity.PacProxyService; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.coverage.CoverageService; import com.android.server.cpu.CpuMonitorService; +import com.android.server.criticalevents.CriticalEventLog; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.devicestate.DeviceStateManagerService; import com.android.server.display.DisplayManagerService; @@ -964,6 +965,7 @@ public final class SystemServer implements Dumpable { // Only update the timeout after starting all the services so that we use // the default timeout to start system server. updateWatchdogTimeout(t); + CriticalEventLog.getInstance().logSystemServerStarted(); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS deleted file mode 100644 index 28aff188dbd8..000000000000 --- a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 847766 -nfuller@google.com -include /core/java/android/app/timedetector/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java index ad09ef0ccdc1..061b8ffa05a2 100644 --- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java @@ -79,7 +79,7 @@ public class SpatializerHelperTest { final AudioDeviceAttributes dev3 = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop"); - doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings(); + doNothing().when(mSpyDeviceBroker).postPersistAudioDeviceSettings(); mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/); // test with single device diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS deleted file mode 100644 index a0f46e172da6..000000000000 --- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 847766 -include /services/core/java/com/android/server/timedetector/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING deleted file mode 100644 index a83d8bf20dc6..000000000000 --- a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.timedetector." - } - ] - } - ] -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS deleted file mode 100644 index d64cbcdc2814..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 847766 -include /services/core/java/com/android/server/timezone/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING deleted file mode 100644 index f59188cb9a52..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING +++ /dev/null @@ -1,13 +0,0 @@ -{ - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ - { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.timezonedetector." - } - ] - } - ] -} diff --git a/services/tests/timetests/Android.bp b/services/tests/timetests/Android.bp new file mode 100644 index 000000000000..23ab85996fff --- /dev/null +++ b/services/tests/timetests/Android.bp @@ -0,0 +1,27 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "FrameworksTimeServicesTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "device-time-shell-utils", + "junit", + "junit-params", + "mockito-target-minus-junit4", + "platform-test-annotations", + "services.core", + "truth", + ], + libs: ["android.test.runner"], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], +} diff --git a/services/tests/timetests/AndroidManifest.xml b/services/tests/timetests/AndroidManifest.xml new file mode 100644 index 000000000000..62fbb05ee1e1 --- /dev/null +++ b/services/tests/timetests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.framework.services.tests.time"> + + <!-- Required for user checks --> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.framework.services.tests.time" + android:label="Frameworks Time Services Tests" /> + +</manifest> diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/timetests/OWNERS index a6ff1ba8a8cb..a6ff1ba8a8cb 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS +++ b/services/tests/timetests/OWNERS diff --git a/services/tests/timetests/TEST_MAPPING b/services/tests/timetests/TEST_MAPPING new file mode 100644 index 000000000000..b24010ce447a --- /dev/null +++ b/services/tests/timetests/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ + { + "name": "FrameworksTimeServicesTests" + } + ] +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java index b6eea461d222..b6eea461d222 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java index 808c1ec2603b..392a48f5a7ca 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java @@ -33,14 +33,14 @@ import android.app.time.TimeConfiguration; import com.android.server.timedetector.TimeDetectorStrategy.Origin; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + import org.junit.Test; import org.junit.runner.RunWith; import java.time.Instant; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - /** * Tests for {@link ConfigurationInternal} and associated {@link TimeCapabilitiesAndConfig} * behavior. diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java index d9bc74dfb1cb..d9bc74dfb1cb 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java +++ b/services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java index a7a9c0cd86f2..a7a9c0cd86f2 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java +++ b/services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java index 57d546929d8b..57d546929d8b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java index c8afb78bc12f..c8afb78bc12f 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java index fcc76d37de2b..fcc76d37de2b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java index d91ee92e005e..d91ee92e005e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java index d5d2cbd0749e..d5d2cbd0749e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java index de5a37b43df8..de5a37b43df8 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 6b2d4b01dd08..6b2d4b01dd08 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index dd58135a8e87..c64ec724b641 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -51,6 +51,9 @@ import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.server.timezonedetector.StateChangeListener; import com.android.server.timezonedetector.TestStateChangeListener; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -62,9 +65,6 @@ import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - @RunWith(JUnitParamsRunner.class) public class TimeDetectorStrategyImplTest { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java index 566c6b03555d..c77fe3943b95 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -35,12 +35,12 @@ import android.app.time.TimeZoneCapabilities; import android.app.time.TimeZoneCapabilitiesAndConfig; import android.app.time.TimeZoneConfiguration; -import org.junit.Test; -import org.junit.runner.RunWith; - import junitparams.JUnitParamsRunner; import junitparams.Parameters; +import org.junit.Test; +import org.junit.runner.RunWith; + /** * Tests for {@link ConfigurationInternal} and associated {@link TimeZoneCapabilitiesAndConfig} * behavior. diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java index fc6afe486187..fc6afe486187 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index 1e72369ac3a6..74393070209e 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -43,7 +43,7 @@ public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { this::notifyChangeListeners); } - public void initializeConfigurationAndStatus( + void initializeConfigurationAndStatus( ConfigurationInternal configuration, TimeZoneDetectorStatus status) { mFakeServiceConfigAccessor.initializeCurrentUserConfiguration(configuration); mStatus = Objects.requireNonNull(status); diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java index 602842addff2..602842addff2 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java index 4c14014405f4..4c14014405f4 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java index 8207c1915edb..8207c1915edb 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java index 3fdac66225a8..3fdac66225a8 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java index b96c82fc396c..72b181984123 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java @@ -29,10 +29,17 @@ import java.util.List; public final class ShellCommandTestSupport { private ShellCommandTestSupport() {} + /** + * Returns a {@link ShellCommand} from the supplied String, where elements of the command are + * separated with spaces. No escaping is performed. + */ public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) { return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" "))); } + /** + * Returns a {@link ShellCommand} from the supplied list of command line elements. + */ public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) { ShellCommand command = mock(ShellCommand.class); class ArgProvider { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java index f45b3a822f1a..67b8cc22fac0 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java @@ -28,6 +28,7 @@ public class TestCallerIdentityInjector implements CallerIdentityInjector { private int mCallingUserId; private Integer mCurrentCallingUserId; + /** Initializes the calling user ID. */ public void initializeCallingUserId(@UserIdInt int userId) { mCallingUserId = userId; mCurrentCallingUserId = userId; diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java index aad06d87f111..61f92602295c 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java @@ -23,6 +23,7 @@ public class TestCurrentUserIdentityInjector implements CurrentUserIdentityInjec private Integer mCurrentUserId; + /** Initializes the current user ID. */ public void initializeCurrentUserId(@UserIdInt int userId) { mCurrentUserId = userId; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java index eb6f00c9250b..eb6f00c9250b 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestState.java index 97095c4f675f..97095c4f675f 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestState.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java index 9cbf0a34f8fe..23f96a21786b 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java @@ -27,6 +27,7 @@ public class TestStateChangeListener implements StateChangeListener { mNotificationsReceived++; } + /** Asserts the expected number of notifications have been received, then resets the count. */ public void assertNotificationsReceivedAndReset(int expectedCount) { assertNotificationsReceived(expectedCount); resetNotificationsReceivedCount(); diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java index 0c78f5b85fac..0c78f5b85fac 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java index a02c8ca001ce..a02c8ca001ce 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index d9d8053e6220..d9d8053e6220 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 03d406f94696..e52e8b60a61d 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -72,6 +72,9 @@ import android.service.timezone.TimeZoneProviderStatus; import com.android.server.SystemTimeZone.TimeZoneConfidence; import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -83,9 +86,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Function; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - /** * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}. */ diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java index f8d169b54bca..e05d84f50465 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java @@ -38,6 +38,7 @@ public final class FakeTimeZoneProviderEventPreProcessor return timeZoneProviderEvent; } + /** Enters a mode where {@link #preProcess} will always return "uncertain" events. */ public void enterUncertainMode() { mIsUncertain = true; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java index e7dd97949bb0..e7dd97949bb0 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java index 7ff015dd8370..7ff015dd8370 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java index 1ae74c679b53..1ae74c679b53 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java index 042e3ef892f9..042e3ef892f9 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java index a3fb5e620c20..a3fb5e620c20 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java index f3440f7c9d1c..f3440f7c9d1c 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 3ba94000d4a5..261b5d33b635 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.app.AutomaticZenRule.TYPE_BEDTIME; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; import static junit.framework.TestCase.assertEquals; @@ -24,9 +25,12 @@ import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; +import android.app.Flags; import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.net.Uri; +import android.os.Parcel; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; @@ -41,6 +45,7 @@ import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.server.UiServiceTestCase; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParserException; @@ -55,6 +60,28 @@ import java.io.IOException; @RunWith(AndroidJUnit4.class) public class ZenModeConfigTest extends UiServiceTestCase { + private final String NAME = "name"; + private final ComponentName OWNER = new ComponentName("pkg", "cls"); + private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act"); + private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build(); + private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme") + .authority("authority") + .appendPath("path") + .appendPath("test") + .build(); + + private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE); + private final String TRIGGER_DESC = "Every Night, 10pm to 6am"; + private final int TYPE = TYPE_BEDTIME; + private final boolean ALLOW_MANUAL = true; + private final int ICON_RES_ID = 1234; + private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS; + private final boolean ENABLED = true; + private final int CREATION_TIME = 123; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Test public void testPriorityOnlyMutingAllNotifications() { ZenModeConfig config = getMutedRingerConfig(); @@ -202,7 +229,59 @@ public class ZenModeConfigTest extends UiServiceTestCase { } @Test - public void testRuleXml() throws Exception { + public void testWriteToParcel() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = CONFIG_ACTIVITY; + rule.component = OWNER; + rule.conditionId = CONDITION_ID; + rule.condition = CONDITION; + rule.enabled = ENABLED; + rule.creationTime = 123; + rule.id = "id"; + rule.zenMode = INTERRUPTION_FILTER; + rule.modified = true; + rule.name = NAME; + rule.snoozing = true; + rule.pkg = OWNER.getPackageName(); + rule.zenPolicy = POLICY; + + rule.allowManualInvocation = ALLOW_MANUAL; + rule.type = TYPE; + rule.iconResId = ICON_RES_ID; + rule.triggerDescription = TRIGGER_DESC; + + Parcel parcel = Parcel.obtain(); + rule.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel); + + assertEquals(rule.pkg, parceled.pkg); + assertEquals(rule.snoozing, parceled.snoozing); + assertEquals(rule.enabler, parceled.enabler); + assertEquals(rule.component, parceled.component); + assertEquals(rule.configurationActivity, parceled.configurationActivity); + assertEquals(rule.condition, parceled.condition); + assertEquals(rule.enabled, parceled.enabled); + assertEquals(rule.creationTime, parceled.creationTime); + assertEquals(rule.modified, parceled.modified); + assertEquals(rule.conditionId, parceled.conditionId); + assertEquals(rule.name, parceled.name); + assertEquals(rule.zenMode, parceled.zenMode); + + assertEquals(rule.allowManualInvocation, parceled.allowManualInvocation); + assertEquals(rule.iconResId, parceled.iconResId); + assertEquals(rule.type, parceled.type); + assertEquals(rule.triggerDescription, parceled.triggerDescription); + assertEquals(rule.zenPolicy, parceled.zenPolicy); + assertEquals(rule, parceled); + assertEquals(rule.hashCode(), parceled.hashCode()); + + } + + @Test + public void testRuleXml_classic() throws Exception { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = new ComponentName("a", "a"); rule.component = new ComponentName("b", "b"); @@ -239,6 +318,58 @@ public class ZenModeConfigTest extends UiServiceTestCase { } @Test + public void testRuleXml() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = CONFIG_ACTIVITY; + rule.component = OWNER; + rule.conditionId = CONDITION_ID; + rule.condition = CONDITION; + rule.enabled = ENABLED; + rule.creationTime = 123; + rule.id = "id"; + rule.zenMode = INTERRUPTION_FILTER; + rule.modified = true; + rule.name = NAME; + rule.snoozing = true; + rule.pkg = OWNER.getPackageName(); + rule.zenPolicy = POLICY; + rule.creationTime = CREATION_TIME; + + rule.allowManualInvocation = ALLOW_MANUAL; + rule.type = TYPE; + rule.iconResId = ICON_RES_ID; + rule.triggerDescription = TRIGGER_DESC; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + writeRuleXml(rule, baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ZenModeConfig.ZenRule fromXml = readRuleXml(bais); + + assertEquals(rule.pkg, fromXml.pkg); + // always resets on reboot + assertFalse(fromXml.snoozing); + //should all match original + assertEquals(rule.component, fromXml.component); + assertEquals(rule.configurationActivity, fromXml.configurationActivity); + assertNull(fromXml.enabler); + assertEquals(rule.condition, fromXml.condition); + assertEquals(rule.enabled, fromXml.enabled); + assertEquals(rule.creationTime, fromXml.creationTime); + assertEquals(rule.modified, fromXml.modified); + assertEquals(rule.conditionId, fromXml.conditionId); + assertEquals(rule.name, fromXml.name); + assertEquals(rule.zenMode, fromXml.zenMode); + assertEquals(rule.creationTime, fromXml.creationTime); + + assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation); + assertEquals(rule.type, fromXml.type); + assertEquals(rule.triggerDescription, fromXml.triggerDescription); + assertEquals(rule.iconResId, fromXml.iconResId); + } + + @Test public void testRuleXml_pkg_component() throws Exception { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = new ComponentName("a", "a"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index bcd807ab6d2f..fd3d5e9bf863 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -23,6 +23,7 @@ import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import android.app.AutomaticZenRule; import android.content.ComponentName; import android.net.Uri; import android.provider.Settings; @@ -229,6 +230,10 @@ public class ZenModeDiffTest extends UiServiceTestCase { rule.name = "name"; rule.snoozing = true; rule.pkg = "a"; + rule.allowManualInvocation = true; + rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME; + rule.iconResId = 123; + rule.triggerDescription = "At night"; return rule; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index e22c10489d4d..0349ad9e30c7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.app.AutomaticZenRule.TYPE_BEDTIME; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; @@ -71,6 +72,7 @@ import android.annotation.SuppressLint; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; @@ -88,6 +90,7 @@ import android.media.VolumePolicy; import android.net.Uri; import android.os.Process; import android.os.UserHandle; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Condition; @@ -120,6 +123,7 @@ import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -150,6 +154,28 @@ public class ZenModeHelperTest extends UiServiceTestCase { private static final int CUSTOM_PKG_UID = 1; private static final String CUSTOM_RULE_ID = "custom_rule"; + private final String NAME = "name"; + private final ComponentName OWNER = new ComponentName("pkg", "cls"); + private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act"); + private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build(); + private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme") + .authority("authority") + .appendPath("path") + .appendPath("test") + .build(); + + private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE); + private final String TRIGGER_DESC = "Every Night, 10pm to 6am"; + private final int TYPE = TYPE_BEDTIME; + private final boolean ALLOW_MANUAL = true; + private final int ICON_RES_ID = 1234; + private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS; + private final boolean ENABLED = true; + private final int CREATION_TIME = 123; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + ConditionProviders mConditionProviders; @Mock NotificationManager mNotificationManager; @Mock PackageManager mPackageManager; @@ -1961,6 +1987,26 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testSetManualZenMode() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + setupZenConfig(); + + // note that caller=null because that's how it comes in from NMS.setZenMode + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", + Process.SYSTEM_UID, true); + + // confirm that setting zen mode via setManualZenMode changed the zen mode correctly + assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode); + assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation); + + // and also that it works to turn it back off again + mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", + Process.SYSTEM_UID, true); + + assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode); + } + + @Test + public void testSetManualZenMode_legacy() { setupZenConfig(); // note that caller=null because that's how it comes in from NMS.setZenMode @@ -2607,6 +2653,47 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom stricter } + @Test + public void testCreateAutomaticZenRule_allFields() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = CONFIG_ACTIVITY; + rule.component = OWNER; + rule.conditionId = CONDITION_ID; + rule.condition = CONDITION; + rule.enabled = ENABLED; + rule.creationTime = 123; + rule.id = "id"; + rule.zenMode = INTERRUPTION_FILTER; + rule.modified = true; + rule.name = NAME; + rule.snoozing = true; + rule.pkg = OWNER.getPackageName(); + rule.zenPolicy = POLICY; + + rule.allowManualInvocation = ALLOW_MANUAL; + rule.type = TYPE; + rule.iconResId = ICON_RES_ID; + rule.triggerDescription = TRIGGER_DESC; + + AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule); + + assertEquals(NAME, actual.getName()); + assertEquals(OWNER, actual.getOwner()); + assertEquals(CONDITION_ID, actual.getConditionId()); + assertEquals(NotificationManager.INTERRUPTION_FILTER_ALARMS, + actual.getInterruptionFilter()); + assertEquals(ENABLED, actual.isEnabled()); + assertEquals(POLICY, actual.getZenPolicy()); + assertEquals(CONFIG_ACTIVITY, actual.getConfigurationActivity()); + assertEquals(TYPE, actual.getType()); + assertEquals(ALLOW_MANUAL, actual.isManualInvocationAllowed()); + assertEquals(CREATION_TIME, actual.getCreationTime()); + assertEquals(OWNER.getPackageName(), actual.getPackageName()); + assertEquals(ICON_RES_ID, actual.getIconResId()); + assertEquals(TRIGGER_DESC, actual.getTriggerDescription()); + } + private void setupZenConfig() { mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF; mZenModeHelper.mConfig.allowAlarms = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 7822071f3933..ec068bed1a3b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -27,12 +27,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -130,17 +126,6 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test - public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() { - clearInvocations(mTransaction); - mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true; - - mTaskFragment.updateOrganizedTaskFragmentSurface(); - - verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat()); - verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt()); - } - - @Test public void testShouldStartChangeTransition_relativePositionChange() { final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 11cbcb1c149d..4b1a72695e50 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -568,6 +568,7 @@ public class ApnSetting implements Parcelable { private final int mSkip464Xlat; private final boolean mAlwaysOn; private final @InfrastructureBitmask int mInfrastructureBitmask; + private final boolean mEsimBootstrapProvisioning; /** * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought @@ -979,6 +980,18 @@ public class ApnSetting implements Parcelable { return mInfrastructureBitmask; } + /** + * Returns esim bootstrap provisioning flag for which the APN can be used on. For example, + * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning + * is being activated. + * + * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise. + * @hide + */ + public boolean isEsimBootstrapProvisioning() { + return mEsimBootstrapProvisioning; + } + private ApnSetting(Builder builder) { this.mEntryName = builder.mEntryName; this.mApnName = builder.mApnName; @@ -1016,6 +1029,7 @@ public class ApnSetting implements Parcelable { this.mSkip464Xlat = builder.mSkip464Xlat; this.mAlwaysOn = builder.mAlwaysOn; this.mInfrastructureBitmask = builder.mInfrastructureBitmask; + this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning; } /** @@ -1097,6 +1111,8 @@ public class ApnSetting implements Parcelable { .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1) .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.INFRASTRUCTURE_BITMASK))) + .setEsimBootstrapProvisioning(cursor.getInt( + cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1) .buildWithoutCheck(); } @@ -1137,6 +1153,7 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(apn.mSkip464Xlat) .setAlwaysOn(apn.mAlwaysOn) .setInfrastructureBitmask(apn.mInfrastructureBitmask) + .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning) .buildWithoutCheck(); } @@ -1184,6 +1201,7 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mAlwaysOn); sb.append(", ").append(mInfrastructureBitmask); sb.append(", ").append(Objects.hash(mUser, mPassword)); + sb.append(", ").append(mEsimBootstrapProvisioning); return sb.toString(); } @@ -1247,7 +1265,7 @@ public class ApnSetting implements Parcelable { mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask, mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat, - mAlwaysOn, mInfrastructureBitmask); + mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning); } @Override @@ -1289,7 +1307,8 @@ public class ApnSetting implements Parcelable { && mCarrierId == other.mCarrierId && mSkip464Xlat == other.mSkip464Xlat && mAlwaysOn == other.mAlwaysOn - && mInfrastructureBitmask == other.mInfrastructureBitmask; + && mInfrastructureBitmask == other.mInfrastructureBitmask + && mEsimBootstrapProvisioning == other.mEsimBootstrapProvisioning; } /** @@ -1340,7 +1359,8 @@ public class ApnSetting implements Parcelable { && Objects.equals(mCarrierId, other.mCarrierId) && Objects.equals(mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(mAlwaysOn, other.mAlwaysOn) - && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask); + && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask) + && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning); } /** @@ -1378,7 +1398,9 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mCarrierId, other.mCarrierId) && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(this.mAlwaysOn, other.mAlwaysOn) - && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask); + && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask) + && Objects.equals(this.mEsimBootstrapProvisioning, + other.mEsimBootstrapProvisioning); } // Equal or one is null. @@ -1451,6 +1473,7 @@ public class ApnSetting implements Parcelable { apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat); apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn); apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask); + apnValue.put(Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning); return apnValue; } @@ -1724,6 +1747,7 @@ public class ApnSetting implements Parcelable { dest.writeInt(mSkip464Xlat); dest.writeBoolean(mAlwaysOn); dest.writeInt(mInfrastructureBitmask); + dest.writeBoolean(mEsimBootstrapProvisioning); } private static ApnSetting readFromParcel(Parcel in) { @@ -1760,6 +1784,7 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(in.readInt()) .setAlwaysOn(in.readBoolean()) .setInfrastructureBitmask(in.readInt()) + .setEsimBootstrapProvisioning(in.readBoolean()) .buildWithoutCheck(); } @@ -1842,6 +1867,7 @@ public class ApnSetting implements Parcelable { private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT; private boolean mAlwaysOn; private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR; + private boolean mEsimBootstrapProvisioning; /** * Default constructor for Builder. @@ -2280,6 +2306,19 @@ public class ApnSetting implements Parcelable { } /** + * Sets esim bootstrap provisioning flag + * + * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap + * provisioning, {@code false} otherwise. + * @hide + */ + @NonNull + public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) { + this.mEsimBootstrapProvisioning = esimBootstrapProvisioning; + return this; + } + + /** * Builds {@link ApnSetting} from this builder. * * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)} diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp new file mode 100644 index 000000000000..afaa3f0f293f --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp @@ -0,0 +1,36 @@ +// 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "ConcurrentMultiSessionImeTest", + srcs: ["src/**/*.java"], + libs: ["android.test.runner"], + static_libs: [ + "androidx.test.ext.junit", + "platform-test-annotations", + "platform-test-rules", + "truth", + ], + test_suites: [ + "general-tests", + ], + sdk_version: "current", + + // Store test artifacts in separated directories for easier debugging. + per_testcase_directory: true, +} diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml new file mode 100644 index 000000000000..0defe5b3f2ff --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.inputmethod.multisessiontest"> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation> +</manifest> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml new file mode 100644 index 000000000000..fd598c568974 --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Config for Concurrent Multi-Session IME tests"> + <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController" + type="module_controller"> + <option name="required-feature" value="android.software.input_methods" /> + + <!-- Currently enabled to automotive only --> + <option name="required-feature" value="android.hardware.type.automotive" /> + </object> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" /> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard" /> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="force-install-mode" value="FULL" /> + <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.inputmethod.multisessiontest" /> + </test> +</configuration> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java new file mode 100644 index 000000000000..b66ceba458ac --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod.multisessiontest; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class ConcurrentMultiUserTest { + + @Before + public void doBeforeEachTest() { + // No op + } + + @Test + public void behaviorBeingTested_expectedResult() { + // Sample test + Context context = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertThat(context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE)).isTrue(); + assertThat(context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_INPUT_METHODS)).isTrue(); + } +} diff --git a/tests/inputmethod/OWNERS b/tests/inputmethod/OWNERS new file mode 100644 index 000000000000..6bb4b17ed4eb --- /dev/null +++ b/tests/inputmethod/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 34867 + +include /services/core/java/com/android/server/inputmethod/OWNERS |